Quuxplusone / LLVMBugzillaTest

0 stars 0 forks source link

Clang does not implement the noclone attribute #22310

Open Quuxplusone opened 9 years ago

Quuxplusone commented 9 years ago
Bugzilla Link PR22311
Status NEW
Importance P enhancement
Reported by Steven Stewart-Gallus (sstewartgallus00@mylangara.bc.ca)
Reported on 2015-01-23 12:41:49 -0800
Last modified on 2015-02-10 20:10:14 -0800
Version 3.5
Hardware PC Linux
CC llvm-bugs@lists.llvm.org, richard-llvm@metafoo.co.uk
Fixed by commit(s)
Attachments
Blocks
Blocked by
See also
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;
}
Quuxplusone commented 9 years ago

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

Quuxplusone 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.

Quuxplusone commented 9 years ago
(In reply to comment #2)
> 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.