Skip to content

Commit 4c35bf3

Browse files
author
Al Viro
committed
[ia64] sanitize elf_access_gpreg()
The function takes the register number, finds the corresponding field of pt_regs for registers that are saved there or does the unwind for the registers that end up spilled on the kernel stack. Then it reads from or writes to the resulting location. Unfortunately, finding the required pt_regs field is done by rather horrible switch. It's microoptimized in all the wrong places - it even uses the knowledge that fields for r8..r11 follow each other in pt_regs layout, while r12..r13 are not adjacent to those, etc. All of that is to encode the mapping from register numbers to offsets + the information that r4..r7 are not to be found in pt_regs. It's deeply in nasal demon territory, at that - the games it plays with pointer arithmetics on addresses of structure members are undefined behaviour. Valid C ends up with better code in this case: just initialize a constant array with offsets of relevant pt_regs fields and we don't need that switch anymore. Signed-off-by: Al Viro <[email protected]>
1 parent b3a9e3b commit 4c35bf3

File tree

1 file changed

+23
-34
lines changed

1 file changed

+23
-34
lines changed

arch/ia64/kernel/ptrace.c

Lines changed: 23 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1273,52 +1273,41 @@ struct regset_getset {
12731273
int ret;
12741274
};
12751275

1276+
static const ptrdiff_t pt_offsets[16] =
1277+
{
1278+
#define R(n) offsetof(struct pt_regs, r##n)
1279+
[0] = -1, R(1), R(2), R(3),
1280+
[4] = -1, [5] = -1, [6] = -1, [7] = -1,
1281+
R(8), R(9), R(10), R(11), R(12), R(13), R(14), R(15),
1282+
#undef R
1283+
};
1284+
12761285
static int
12771286
access_elf_gpreg(struct task_struct *target, struct unw_frame_info *info,
12781287
unsigned long addr, unsigned long *data, int write_access)
12791288
{
1280-
struct pt_regs *pt;
1281-
unsigned long *ptr = NULL;
1282-
int ret;
1283-
char nat = 0;
1289+
struct pt_regs *pt = task_pt_regs(target);
1290+
unsigned reg = addr / sizeof(unsigned long);
1291+
ptrdiff_t d = pt_offsets[reg];
12841292

1285-
pt = task_pt_regs(target);
1286-
switch (addr) {
1287-
case ELF_GR_OFFSET(1):
1288-
ptr = &pt->r1;
1289-
break;
1290-
case ELF_GR_OFFSET(2):
1291-
case ELF_GR_OFFSET(3):
1292-
ptr = (void *)&pt->r2 + (addr - ELF_GR_OFFSET(2));
1293-
break;
1294-
case ELF_GR_OFFSET(4) ... ELF_GR_OFFSET(7):
1293+
if (d >= 0) {
1294+
unsigned long *ptr = (void *)pt + d;
1295+
if (write_access)
1296+
*ptr = *data;
1297+
else
1298+
*data = *ptr;
1299+
return 0;
1300+
} else {
1301+
char nat = 0;
12951302
if (write_access) {
12961303
/* read NaT bit first: */
12971304
unsigned long dummy;
1298-
1299-
ret = unw_get_gr(info, addr/8, &dummy, &nat);
1305+
int ret = unw_get_gr(info, reg, &dummy, &nat);
13001306
if (ret < 0)
13011307
return ret;
13021308
}
1303-
return unw_access_gr(info, addr/8, data, &nat, write_access);
1304-
case ELF_GR_OFFSET(8) ... ELF_GR_OFFSET(11):
1305-
ptr = (void *)&pt->r8 + addr - ELF_GR_OFFSET(8);
1306-
break;
1307-
case ELF_GR_OFFSET(12):
1308-
case ELF_GR_OFFSET(13):
1309-
ptr = (void *)&pt->r12 + addr - ELF_GR_OFFSET(12);
1310-
break;
1311-
case ELF_GR_OFFSET(14):
1312-
ptr = &pt->r14;
1313-
break;
1314-
case ELF_GR_OFFSET(15):
1315-
ptr = &pt->r15;
1309+
return unw_access_gr(info, reg, data, &nat, write_access);
13161310
}
1317-
if (write_access)
1318-
*ptr = *data;
1319-
else
1320-
*data = *ptr;
1321-
return 0;
13221311
}
13231312

13241313
static int

0 commit comments

Comments
 (0)