Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'parisc-4.4-4' of git://git.kernel.org/pub/scm/linux/kernel/git/deller/parisc-linux

Pull parisc system call restart fix from Helge Deller:
"The architectural design of parisc always uses two instructions to
call kernel syscalls (delayed branch feature). This means that the
instruction following the branch (located in the delay slot of the
branch instruction) is executed before control passes to the branch
destination.

Depending on which assembler instruction and how it is used in
usersapce in the delay slot, this sometimes made restarted syscalls
like futex() and poll() failing with -ENOSYS"

* 'parisc-4.4-4' of git://git.kernel.org/pub/scm/linux/kernel/git/deller/parisc-linux:
parisc: Fix syscall restarts

+52 -12
+52 -12
arch/parisc/kernel/signal.c
··· 435 435 regs->gr[28]); 436 436 } 437 437 438 + /* 439 + * Check how the syscall number gets loaded into %r20 within 440 + * the delay branch in userspace and adjust as needed. 441 + */ 442 + 443 + static void check_syscallno_in_delay_branch(struct pt_regs *regs) 444 + { 445 + u32 opcode, source_reg; 446 + u32 __user *uaddr; 447 + int err; 448 + 449 + /* Usually we don't have to restore %r20 (the system call number) 450 + * because it gets loaded in the delay slot of the branch external 451 + * instruction via the ldi instruction. 452 + * In some cases a register-to-register copy instruction might have 453 + * been used instead, in which case we need to copy the syscall 454 + * number into the source register before returning to userspace. 455 + */ 456 + 457 + /* A syscall is just a branch, so all we have to do is fiddle the 458 + * return pointer so that the ble instruction gets executed again. 459 + */ 460 + regs->gr[31] -= 8; /* delayed branching */ 461 + 462 + /* Get assembler opcode of code in delay branch */ 463 + uaddr = (unsigned int *) ((regs->gr[31] & ~3) + 4); 464 + err = get_user(opcode, uaddr); 465 + if (err) 466 + return; 467 + 468 + /* Check if delay branch uses "ldi int,%r20" */ 469 + if ((opcode & 0xffff0000) == 0x34140000) 470 + return; /* everything ok, just return */ 471 + 472 + /* Check if delay branch uses "nop" */ 473 + if (opcode == INSN_NOP) 474 + return; 475 + 476 + /* Check if delay branch uses "copy %rX,%r20" */ 477 + if ((opcode & 0xffe0ffff) == 0x08000254) { 478 + source_reg = (opcode >> 16) & 31; 479 + regs->gr[source_reg] = regs->gr[20]; 480 + return; 481 + } 482 + 483 + pr_warn("syscall restart: %s (pid %d): unexpected opcode 0x%08x\n", 484 + current->comm, task_pid_nr(current), opcode); 485 + } 486 + 438 487 static inline void 439 488 syscall_restart(struct pt_regs *regs, struct k_sigaction *ka) 440 489 { ··· 506 457 } 507 458 /* fallthrough */ 508 459 case -ERESTARTNOINTR: 509 - /* A syscall is just a branch, so all 510 - * we have to do is fiddle the return pointer. 511 - */ 512 - regs->gr[31] -= 8; /* delayed branching */ 460 + check_syscallno_in_delay_branch(regs); 513 461 break; 514 462 } 515 463 } ··· 556 510 } 557 511 case -ERESTARTNOHAND: 558 512 case -ERESTARTSYS: 559 - case -ERESTARTNOINTR: { 560 - /* Hooray for delayed branching. We don't 561 - * have to restore %r20 (the system call 562 - * number) because it gets loaded in the delay 563 - * slot of the branch external instruction. 564 - */ 565 - regs->gr[31] -= 8; 513 + case -ERESTARTNOINTR: 514 + check_syscallno_in_delay_branch(regs); 566 515 return; 567 - } 568 516 default: 569 517 break; 570 518 }