Re: [Toybox] fun with vfork

2016-10-13 Thread Rob Landley
On 10/13/2016 03:25 AM, Josh Gao wrote:
> On Wed, Oct 12, 2016 at 5:49 PM, Rob Landley  > wrote:
> 
> So... no? I think? Is there a way _I_ can tag this? (I can't do my own
> vfork prototype because I can't #undef the one I get out of unistd.h and
> that's a fairly generic header. It's sad I can't redo function
> prototypes after the fact, but the language never gave me a way to.
> Maybe I could do a gratuitous wrapper around it?)
> 
> 
> gcc seems to do the equivalent automatically for any function named
> "vfork": 
> https://github.com/gcc-mirror/gcc/blob/e11be3ea01eaf8acd8cd86d3f9c427621b64e6b4/gcc/calls.c#L533

If so, it's not working.

> On Thu, Oct 13, 2016 at 1:03 AM, Rob Landley  > wrote:
> 
> If I have to gratuitously call setjmp() and ignore its return value
> right before calling vfork() to beat reliable behavior out of gcc, I can
> do that. I can also use global variables instead of local variables, or
> make a structure of local variables so gcc can't gratuitously reorganize
> them and trim the stack, or have my one allowed phone
> function call be to a function I define that contains "everything the
> child does" to preserve the stack context.
> 
> Personally, I'd rather the compiler didn't fight me when I'm trying to
> do something obvious, but I have LOTS of ways to fight back. :)
> 
>  
> It doesn't seem like gcc differentiates between vfork, setjmp, etc. so
> it's presumably providing some behavior that satisfies the constraints
> of all of them (or there's a bug).

Given how few gcc developers do nommu development, I'm amazed there
isn't even more bit-rot here.

> The specification for longjmp says
> that only non-volatile local variables that get modified have
> unspecified values, so you could maybe try sprinkling volatile on things
> to see if it makes your problem go away?

That's not an approach I'm comfortable with.

Rich and I were arguing about this on IRC. I tend to respond to this
sort of thing with research, trying to understand what the system is
actually doing under the covers and what behavior the kernel provides
(and whether it falls under the "stable ABI" guarantees), and then build
up from there. If I have to call clone() myself to GET a stable
guaranteed ABI, I'm ok with that. Dig down, find the problem, and fix it.

But ever since C++ developers took over compiler development and started
rewriting C compilers in C++, they've tried to turn C into C++. C is a
portable assembly language with minimal abstraction between what your
program says and what the machine does. C++ is a giant pile of magic
behavior handed down from the gods and only understandable to the
sufficiently initiated so there's piles of "you must do this, ask not
why" all over the place.

Because of this the compiler "optimizing" away a memset() so your
encryption keys stay in memory, or saying you can't typecast and
dereference something without a gratuitous intermediate variable, is now
expected, and the range of such unclean behavior is ever-expanding.

Before they started rewriting compilers in C++? Not so much. Bits of
modern compilers are untrustworthy, and that is a PROBLEM, not something
to just accept. I want to hit them with a large enough brick to make
them behave. Disabling entire categories of optimization come to mind,
although I haven't found a way to do that here because I depend on dead
code elimination.

I don't like sprinkling typecasts around to fix problems. "volatile"
falls under the same heading: the kernel guys use memory barriers
instead of volatile because volatile is almost meaningless.

Rich is trying to convince me to use posix_spawn(), a function so
obscure Ubuntu 14.04 hasn't even got a man page for it:

  $ man posix_
  posix_fadviseposix_fallocate  posix_memalign   posix_openpt
  $ man posix_spawn
  No manual entry for posix_spawn

His argument seems to be that posix_spawn() is magic that can't be
implemented in C, which is odd because musl-libc is implemented in C. I
just checked and under the covers, posix_spawn() is calling clone()
itself, with the normal VFORK flags but providing its own (1024 byte) stack.

*shrug* I can do that. Or I could do my "one function call" approach so
the compiler's liveness analysis of local variables doesn't come into
play. (I HAVE to be able to do one function call after vfork with
semi-arbitrary complexity IN that function call, or I couldn't do
execlp(), which is implemented as a loop trying all the $PATH locations.
That counts as "vfork and exec". So the logical next bigger hammer is
"wrapper function". Of course to avoid the compiler screwing THAT up I
have to disable automatic inlining even in LTO mode, which is the next
round of C++ developers proving C is no better than C++ by breaking it
and going "see!?!??!". Possibly dereferencing a function pointer is
sufficient to force this, let's 

Re: [Toybox] fun with vfork

2016-10-13 Thread Josh Gao
On Wed, Oct 12, 2016 at 5:49 PM, Rob Landley  wrote:
>
> So... no? I think? Is there a way _I_ can tag this? (I can't do my own
> vfork prototype because I can't #undef the one I get out of unistd.h and
> that's a fairly generic header. It's sad I can't redo function
> prototypes after the fact, but the language never gave me a way to.
> Maybe I could do a gratuitous wrapper around it?)
>

gcc seems to do the equivalent automatically for any function named
"vfork":
https://github.com/gcc-mirror/gcc/blob/e11be3ea01eaf8acd8cd86d3f9c427621b64e6b4/gcc/calls.c#L533

On Thu, Oct 13, 2016 at 1:03 AM, Rob Landley  wrote:
>
> If I have to gratuitously call setjmp() and ignore its return value
> right before calling vfork() to beat reliable behavior out of gcc, I can
> do that. I can also use global variables instead of local variables, or
> make a structure of local variables so gcc can't gratuitously reorganize
> them and trim the stack, or have my one allowed phone
> function call be to a function I define that contains "everything the
> child does" to preserve the stack context.
>
> Personally, I'd rather the compiler didn't fight me when I'm trying to
> do something obvious, but I have LOTS of ways to fight back. :)


It doesn't seem like gcc differentiates between vfork, setjmp, etc. so it's
presumably providing some behavior that satisfies the constraints of all of
them (or there's a bug). The specification for longjmp says that only
non-volatile local variables that get modified have unspecified values, so
you could maybe try sprinkling volatile on things to see if it makes your
problem go away?
___
Toybox mailing list
Toybox@lists.landley.net
http://lists.landley.net/listinfo.cgi/toybox-landley.net


Re: [Toybox] fun with vfork

2016-10-13 Thread Rob Landley
On 10/12/2016 12:09 PM, David R. Hedges wrote:
> For what it's worth, your example (and the resulting symptom) seem to
> fall squarely in this undefined territory from the vfork manpage / POSIX
> description:
>> the behavior is undefined if the process created by vfork() [..] calls
>> any other function before successfully calling [..] one of the exec(3)
>> family of functions
> 
> This doesn't solve your problem, but I wanted to make sure you're aware
> of (and deliberately chose to accept) that.

I know what behavior the Linux kernel is producing, based on clone() flags.

If I have to gratuitously call setjmp() and ignore its return value
right before calling vfork() to beat reliable behavior out of gcc, I can
do that. I can also use global variables instead of local variables, or
make a structure of local variables so gcc can't gratuitously reorganize
them and trim the stack, or have my one allowed phone
function call be to a function I define that contains "everything the
child does" to preserve the stack context.

Personally, I'd rather the compiler didn't fight me when I'm trying to
do something obvious, but I have LOTS of ways to fight back. :)

Rob
___
Toybox mailing list
Toybox@lists.landley.net
http://lists.landley.net/listinfo.cgi/toybox-landley.net


Re: [Toybox] fun with vfork

2016-10-12 Thread Rob Landley
On 10/11/2016 12:04 PM, Josh Gao wrote:
> Do you have vfork tagged with __attribute__((returns_twice))? AFAIK,
> that's the incantation to get gcc to do the right thing for setjmpy
> functions.

Do I have it tagged, or do you mean does glibc? (I didn't implement
vfork, it came with libc.) Let's see, grep says glibc in Ubuntu 14.04
just has:

  /usr/include/unistd.h:extern __pid_t vfork (void) __THROW;

And checking musl...

  pid_t vfork(void);

In fact grep -r returns_twice is not finding any instances of it under
/usr/include or under the musl source dir.

So... no? I think? Is there a way _I_ can tag this? (I can't do my own
vfork prototype because I can't #undef the one I get out of unistd.h and
that's a fairly generic header. It's sad I can't redo function
prototypes after the fact, but the language never gave me a way to.
Maybe I could do a gratuitous wrapper around it?)

How does setjmp() itself do this...

/usr/include/setjmp.h:extern int setjmp (jmp_buf __env) __THROWNL;

And sys/cdefs.h #defines __THROWNL as nothing for non-c++ builds.

Rob
___
Toybox mailing list
Toybox@lists.landley.net
http://lists.landley.net/listinfo.cgi/toybox-landley.net


Re: [Toybox] fun with vfork

2016-10-11 Thread Josh Gao
Do you have vfork tagged with __attribute__((returns_twice))? AFAIK, that's
the incantation to get gcc to do the right thing for setjmpy functions.

On Oct 11, 2016 4:23 AM, "Rob Landley"  wrote:

> While doing the rest of nommu support in netcat -L, I had some variant of:
>
>   function()
>   {
> int child, blah = 3;
>
> for (;;) {
>   crunch(blah);
>   child = vfork();
>   if (child<1) break;
> }
> thingy();
>
> execlp(stuff);
>   }
>
> And gcc's optimizer went "blah isn't used anymore after the for loop,
> I'll trim the stack frame down so the return address in the call to
> thingy() in the child overwrites it, and then when vfork returns it's
> corrupted in the parent and the next call to crunch() goes bye-bye".
> Because gcc's optimizer does not understand vfork()'s impact on
> "liveness analysis". (You can think of vfork() as a setjmp that will
> fork() when it hits the next exec or exit, and then the parent process
> longjmp()s back to the stack until the child. But gcc's optimizer doesn't.)
>
> The fix is to add an unnecessary use of blah to the end of the function
> to let it know it's still %*#(%&& used, but then I need a GREAT BIG
> COMMENT to explain why so it isn't removed in future cleanup passes. And
> every other variable potentially has that same problem.
>
> As usual, I want to punch gcc's optimizer in the face and go "DO WHAT I
> TOLD YOU TO DO, DON'T MAKE STUFF UP!" but it never listens. (Do I have
> to start building everything with -O0? What optimization level gives me
> dead code elimination and nothing else?)
>
> Rob
>
> P.S. I'm always amused by the go/rust/swift developers who haven't hit
> their language with anything like the range of use cases you get in C,
> confidently stating that they have yet to see such strange corner cases
> in _their_ language yet. Uh-huh. There's a reason for that and it's
> probably not the one you think.
> ___
> Toybox mailing list
> Toybox@lists.landley.net
> http://lists.landley.net/listinfo.cgi/toybox-landley.net
>
___
Toybox mailing list
Toybox@lists.landley.net
http://lists.landley.net/listinfo.cgi/toybox-landley.net