llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
28.78k stars 11.9k forks source link

Clang does not implement the noclone attribute #22685

Open llvmbot opened 9 years ago

llvmbot commented 9 years ago
Bugzilla Link 22311
Version 3.5
OS Linux
Reporter LLVM Bugzilla Contributor
CC @zygoloid

Extended Description

noclone is a companion attribute to the noinline attribute that GCC implements. It prevents constant propagation from happening. If one is doing magic low level hackery then it is a necessity. A specific use case I have for this is creating a safe wrapper for vfork (which most compilers understandably mess up on).

The wrapper:

attribute((noinline)) attribute((noclone)) attribute((no_sanitize_address)) static pid_t safe_vfork(int (f)(void ), void * arg) { atomic_signal_fence(ATOMIC_SEQ_CST);

    pid_t child = vfork();
    if (0 == child)
            _Exit(f(arg));

    return child;

}

Some workarounds:

Use assembly: This is annoying but would work. Put safe_vfork in a separate module: This doesn't work with link-time optimization but is the typical workaround. Use volatile on the function arguments as follows: Strangely, this doesn't actually work. I can further work around this with more indirection but I am hesitant to do so because the semantics of volatile are very unclear and compiler specific.

attribute((noinline)) attribute((noclone)) attribute((no_sanitize_address)) static pid_t safe_vfork(int (volatile f)(void ), void * volatile arg) { atomic_signal_fence(ATOMIC_SEQ_CST);

    pid_t child = vfork();
    if (0 == child)
            _Exit(f(arg));

    return child;

}

llvmbot commented 9 years ago

Can you explain why you think noclone is important in this example? As far as I can see, you're violating the rules for using vfork:

"the behavior is undefined if the process created by vfork() [...] calls any other function before successfully calling _exit(2) or one of the exec(3) family of functions"

Your call to 'f' here is breaking this rule.

You are correct that this does not obey the version of the POSIX standard that mentions vfork. However, the next version of the POSIX standard removed vfork from the standard as it was too finicky too use and difficult for compilers to work around. Clang itself miscompiles code that uses vfork in a conformant way to that version of the POSIX standard. Because Clang miscompiles conformant code using vfork, I don't expect Clang to change that and many other compilers could miscompile the code anyways so I wrote my little wrapper that tries to carefully isolate away some of the unsafety of vfork. I don't feel bad about breaking that outdated version of the POSIX standard because following that version of the standard does not work.

ec04fc15-fa35-46f2-80e1-5d271f2ef708 commented 9 years ago

Can you explain why you think noclone is important in this example? As far as I can see, you're violating the rules for using vfork:

"the behavior is undefined if the process created by vfork() [...] calls any other function before successfully calling _exit(2) or one of the exec(3) family of functions"

Your call to 'f' here is breaking this rule.

llvmbot commented 9 years ago

Nevermind the volatile workaround does work. I am still cautious of it because the semantics are unclear though.