Skip to content

Commit a918d32

Browse files
mpegregkh
authored andcommitted
powerpc: Fix DAR reporting when alignment handler faults
commit f9effe925039cf54489b5c04e0d40073bb3a123d upstream. Anton noticed that if we fault part way through emulating an unaligned instruction, we don't update the DAR to reflect that. The DAR value is eventually reported back to userspace as the address in the SEGV signal, and if userspace is using that value to demand fault then it can be confused by us not setting the value correctly. This patch is ugly as hell, but is intended to be the minimal fix and back ports easily. Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> Reviewed-by: Paul Mackerras <paulus@ozlabs.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent c53f016 commit a918d32

1 file changed

Lines changed: 74 additions & 45 deletions

File tree

arch/powerpc/kernel/align.c

Lines changed: 74 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,28 @@ static int emulate_dcbz(struct pt_regs *regs, unsigned char __user *addr)
236236

237237
#define SWIZ_PTR(p) ((unsigned char __user *)((p) ^ swiz))
238238

239+
#define __get_user_or_set_dar(_regs, _dest, _addr) \
240+
({ \
241+
int rc = 0; \
242+
typeof(_addr) __addr = (_addr); \
243+
if (__get_user_inatomic(_dest, __addr)) { \
244+
_regs->dar = (unsigned long)__addr; \
245+
rc = -EFAULT; \
246+
} \
247+
rc; \
248+
})
249+
250+
#define __put_user_or_set_dar(_regs, _src, _addr) \
251+
({ \
252+
int rc = 0; \
253+
typeof(_addr) __addr = (_addr); \
254+
if (__put_user_inatomic(_src, __addr)) { \
255+
_regs->dar = (unsigned long)__addr; \
256+
rc = -EFAULT; \
257+
} \
258+
rc; \
259+
})
260+
239261
static int emulate_multiple(struct pt_regs *regs, unsigned char __user *addr,
240262
unsigned int reg, unsigned int nb,
241263
unsigned int flags, unsigned int instr,
@@ -264,9 +286,10 @@ static int emulate_multiple(struct pt_regs *regs, unsigned char __user *addr,
264286
} else {
265287
unsigned long pc = regs->nip ^ (swiz & 4);
266288

267-
if (__get_user_inatomic(instr,
268-
(unsigned int __user *)pc))
289+
if (__get_user_or_set_dar(regs, instr,
290+
(unsigned int __user *)pc))
269291
return -EFAULT;
292+
270293
if (swiz == 0 && (flags & SW))
271294
instr = cpu_to_le32(instr);
272295
nb = (instr >> 11) & 0x1f;
@@ -310,31 +333,31 @@ static int emulate_multiple(struct pt_regs *regs, unsigned char __user *addr,
310333
((nb0 + 3) / 4) * sizeof(unsigned long));
311334

312335
for (i = 0; i < nb; ++i, ++p)
313-
if (__get_user_inatomic(REG_BYTE(rptr, i ^ bswiz),
314-
SWIZ_PTR(p)))
336+
if (__get_user_or_set_dar(regs, REG_BYTE(rptr, i ^ bswiz),
337+
SWIZ_PTR(p)))
315338
return -EFAULT;
316339
if (nb0 > 0) {
317340
rptr = &regs->gpr[0];
318341
addr += nb;
319342
for (i = 0; i < nb0; ++i, ++p)
320-
if (__get_user_inatomic(REG_BYTE(rptr,
321-
i ^ bswiz),
322-
SWIZ_PTR(p)))
343+
if (__get_user_or_set_dar(regs,
344+
REG_BYTE(rptr, i ^ bswiz),
345+
SWIZ_PTR(p)))
323346
return -EFAULT;
324347
}
325348

326349
} else {
327350
for (i = 0; i < nb; ++i, ++p)
328-
if (__put_user_inatomic(REG_BYTE(rptr, i ^ bswiz),
329-
SWIZ_PTR(p)))
351+
if (__put_user_or_set_dar(regs, REG_BYTE(rptr, i ^ bswiz),
352+
SWIZ_PTR(p)))
330353
return -EFAULT;
331354
if (nb0 > 0) {
332355
rptr = &regs->gpr[0];
333356
addr += nb;
334357
for (i = 0; i < nb0; ++i, ++p)
335-
if (__put_user_inatomic(REG_BYTE(rptr,
336-
i ^ bswiz),
337-
SWIZ_PTR(p)))
358+
if (__put_user_or_set_dar(regs,
359+
REG_BYTE(rptr, i ^ bswiz),
360+
SWIZ_PTR(p)))
338361
return -EFAULT;
339362
}
340363
}
@@ -346,29 +369,32 @@ static int emulate_multiple(struct pt_regs *regs, unsigned char __user *addr,
346369
* Only POWER6 has these instructions, and it does true little-endian,
347370
* so we don't need the address swizzling.
348371
*/
349-
static int emulate_fp_pair(unsigned char __user *addr, unsigned int reg,
350-
unsigned int flags)
372+
static int emulate_fp_pair(struct pt_regs *regs, unsigned char __user *addr,
373+
unsigned int reg, unsigned int flags)
351374
{
352375
char *ptr0 = (char *) &current->thread.TS_FPR(reg);
353376
char *ptr1 = (char *) &current->thread.TS_FPR(reg+1);
354-
int i, ret, sw = 0;
377+
int i, sw = 0;
355378

356379
if (reg & 1)
357380
return 0; /* invalid form: FRS/FRT must be even */
358381
if (flags & SW)
359382
sw = 7;
360-
ret = 0;
383+
361384
for (i = 0; i < 8; ++i) {
362385
if (!(flags & ST)) {
363-
ret |= __get_user(ptr0[i^sw], addr + i);
364-
ret |= __get_user(ptr1[i^sw], addr + i + 8);
386+
if (__get_user_or_set_dar(regs, ptr0[i^sw], addr + i))
387+
return -EFAULT;
388+
if (__get_user_or_set_dar(regs, ptr1[i^sw], addr + i + 8))
389+
return -EFAULT;
365390
} else {
366-
ret |= __put_user(ptr0[i^sw], addr + i);
367-
ret |= __put_user(ptr1[i^sw], addr + i + 8);
391+
if (__put_user_or_set_dar(regs, ptr0[i^sw], addr + i))
392+
return -EFAULT;
393+
if (__put_user_or_set_dar(regs, ptr1[i^sw], addr + i + 8))
394+
return -EFAULT;
368395
}
369396
}
370-
if (ret)
371-
return -EFAULT;
397+
372398
return 1; /* exception handled and fixed up */
373399
}
374400

@@ -378,24 +404,27 @@ static int emulate_lq_stq(struct pt_regs *regs, unsigned char __user *addr,
378404
{
379405
char *ptr0 = (char *)&regs->gpr[reg];
380406
char *ptr1 = (char *)&regs->gpr[reg+1];
381-
int i, ret, sw = 0;
407+
int i, sw = 0;
382408

383409
if (reg & 1)
384410
return 0; /* invalid form: GPR must be even */
385411
if (flags & SW)
386412
sw = 7;
387-
ret = 0;
413+
388414
for (i = 0; i < 8; ++i) {
389415
if (!(flags & ST)) {
390-
ret |= __get_user(ptr0[i^sw], addr + i);
391-
ret |= __get_user(ptr1[i^sw], addr + i + 8);
416+
if (__get_user_or_set_dar(regs, ptr0[i^sw], addr + i))
417+
return -EFAULT;
418+
if (__get_user_or_set_dar(regs, ptr1[i^sw], addr + i + 8))
419+
return -EFAULT;
392420
} else {
393-
ret |= __put_user(ptr0[i^sw], addr + i);
394-
ret |= __put_user(ptr1[i^sw], addr + i + 8);
421+
if (__put_user_or_set_dar(regs, ptr0[i^sw], addr + i))
422+
return -EFAULT;
423+
if (__put_user_or_set_dar(regs, ptr1[i^sw], addr + i + 8))
424+
return -EFAULT;
395425
}
396426
}
397-
if (ret)
398-
return -EFAULT;
427+
399428
return 1; /* exception handled and fixed up */
400429
}
401430
#endif /* CONFIG_PPC64 */
@@ -688,9 +717,14 @@ static int emulate_vsx(unsigned char __user *addr, unsigned int reg,
688717
for (j = 0; j < length; j += elsize) {
689718
for (i = 0; i < elsize; ++i) {
690719
if (flags & ST)
691-
ret |= __put_user(ptr[i^sw], addr + i);
720+
ret = __put_user_or_set_dar(regs, ptr[i^sw],
721+
addr + i);
692722
else
693-
ret |= __get_user(ptr[i^sw], addr + i);
723+
ret = __get_user_or_set_dar(regs, ptr[i^sw],
724+
addr + i);
725+
726+
if (ret)
727+
return ret;
694728
}
695729
ptr += elsize;
696730
#ifdef __LITTLE_ENDIAN__
@@ -740,7 +774,7 @@ int fix_alignment(struct pt_regs *regs)
740774
unsigned int dsisr;
741775
unsigned char __user *addr;
742776
unsigned long p, swiz;
743-
int ret, i;
777+
int i;
744778
union data {
745779
u64 ll;
746780
double dd;
@@ -923,7 +957,7 @@ int fix_alignment(struct pt_regs *regs)
923957
if (flags & F) {
924958
/* Special case for 16-byte FP loads and stores */
925959
PPC_WARN_ALIGNMENT(fp_pair, regs);
926-
return emulate_fp_pair(addr, reg, flags);
960+
return emulate_fp_pair(regs, addr, reg, flags);
927961
} else {
928962
#ifdef CONFIG_PPC64
929963
/* Special case for 16-byte loads and stores */
@@ -953,15 +987,12 @@ int fix_alignment(struct pt_regs *regs)
953987
}
954988

955989
data.ll = 0;
956-
ret = 0;
957990
p = (unsigned long)addr;
958991

959992
for (i = 0; i < nb; i++)
960-
ret |= __get_user_inatomic(data.v[start + i],
961-
SWIZ_PTR(p++));
962-
963-
if (unlikely(ret))
964-
return -EFAULT;
993+
if (__get_user_or_set_dar(regs, data.v[start + i],
994+
SWIZ_PTR(p++)))
995+
return -EFAULT;
965996

966997
} else if (flags & F) {
967998
data.ll = current->thread.TS_FPR(reg);
@@ -1031,15 +1062,13 @@ int fix_alignment(struct pt_regs *regs)
10311062
break;
10321063
}
10331064

1034-
ret = 0;
10351065
p = (unsigned long)addr;
10361066

10371067
for (i = 0; i < nb; i++)
1038-
ret |= __put_user_inatomic(data.v[start + i],
1039-
SWIZ_PTR(p++));
1068+
if (__put_user_or_set_dar(regs, data.v[start + i],
1069+
SWIZ_PTR(p++)))
1070+
return -EFAULT;
10401071

1041-
if (unlikely(ret))
1042-
return -EFAULT;
10431072
} else if (flags & F)
10441073
current->thread.TS_FPR(reg) = data.ll;
10451074
else

0 commit comments

Comments
 (0)