[compiler-rt] [sanitizer_common][PowerPC64] Fix internal_clone() error handling (PR #99908)
Ulrich Weigand via llvm-commits
llvm-commits at lists.llvm.org
Fri Jul 26 06:23:27 PDT 2024
================
@@ -1630,13 +1630,22 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
"sc\n\t"
/* Return to parent */
+ "0:\n\t"
+ "neg %0,3\n\t"
+ "b 2f\n\t"
"1:\n\t"
- "mr %0, 3\n\t"
+ "mr %0,3\n\t"
+ "2:\n\t"
+
: "=r"(res)
: "0"(-1), "i"(EINVAL), "i"(__NR_clone), "i"(__NR_exit), "r"(__fn),
"r"(__cstack), "r"(__flags), "r"(__arg), "r"(__ptidptr), "r"(__newtls),
"r"(__ctidptr), "i"(FRAME_SIZE), "i"(FRAME_TOC_SAVE_OFFSET)
: "cr0", "cr1", "memory", "ctr", "r0", "r27", "r28", "r29");
+ if ((uptr)res >= (uptr)-4095) {
----------------
uweigand wrote:
This is interesting. First, we see that the implementation of `internal_iserror` has to match the implementation of `internal_syscall` as far as error handling is concerned. There seem to be two distinct approaches here:
- There is the implementation in `sanitizer_syscall_generic.inc`, which just uses the platform's `syscall` interface. This (at least on Linux) is defined to indicate an error by a return value of -1 and an error code in `errno`.
- Some (but not all) Linux architectures use a different implementation that uses inline asm to perform the system call. On the platforms where this is done, error is indicated by a small negative value (which corresponds to the negated error code). Most of those platforms indicate in a comment that this way is preferable as it does not clobber the libc `errno` variable.
Of course, either way is fine as long as the two match, which is the case as `internal_iserror` and `internal_syscall` are defined in the same architecture-specific file.
However, then there is one "magic" system call `internal_clone`. There is no generic Linux version of this, but only inline asm versions. Note that the caller of `internal_clone` uses the `internal_iserror` routine on its return value, so it implicitly expects `internal_clone` to signal errors in the same way as `internal_syscall`. For those platforms where `internal_syscall` is also an inline asm, this assumption seems to be correct.
However, there are some platforms that use the *generic* `internal_syscall` (and `internal_iserror`) implementation *and* an inline asm version of `internal_clone` - as far as I can see, these are mips, powerpc64, i386, and s390x. For these, there seems to be a disconnect - the return value does *not* match the expected behavior, so a failing `internal_clone` call will likely not be detected correctly.
This bug was fixed for s390x here: https://github.com/llvm/llvm-project/commit/4c32e3d967ecbc4e7f90fef546b5ed38bbd2f7a6, but the other platforms still appear broken to me. The PR looks like an attempt to fix powerpc64 along those same lines - note that for a complete fix, the `return -EINVAL` near the top of the function would also have to be updated.
In general, it seems that for the platforms in this group, we either need to fix `internal_clone` to behave like `syscall` (and set `errno`), or else introduce a platform-specific `internal_syscall` and `internal_iserror` that avoid touching `errno` completely (if that is preferable).
https://github.com/llvm/llvm-project/pull/99908
More information about the llvm-commits
mailing list