Created attachment 1868
Patch (v0) against process.d
Could not yet test this code. That is how I would implement those D wrappers.
Comment #6 by dfj1esp02 — 2023-08-28T08:26:40Z
Doesn't fork break the GC heap? If you want to allocate from GC after fork, it may not work.
Comment #7 by kdevel — 2023-08-29T21:31:49Z
Created attachment 1888
D-Program execv-recur.d demonstrating the memory corruption
//
// execv-recur.d
// 2023-08-29 stvo
//
// disable the GC with
//
// DRT_GCOPT=disable:1 ./execv-recur 1
//
// this program then expectedly reports
//
// rc = <-1> errno = <7> strerror = <Argument list too long>
//
// If run with enabled GC
//
// ./execv-recur 1
//
// the memory gets corrupted and the program reports
//
// [email protected](49): exp <4> actual <131072>
//
Comment #8 by kdevel — 2023-08-29T21:36:16Z
(In reply to anonymous4 from comment #6)
> Doesn't fork break the GC heap? If you want to allocate from GC after fork,
> it may not work.
The corruption does not require a previous call to fork.
Comment #9 by schveiguy — 2023-08-30T08:51:51Z
(In reply to anonymous4 from comment #6)
> Doesn't fork break the GC heap? If you want to allocate from GC after fork,
> it may not work.
fork *can* hang if another thread is holding the GC lock. But we can potentially work around this if we take the GC lock before calling fork.
But using the GC has always been a part of this, it's not new. The toAStringz is using the GC. The problem with the original change is it moved the allocation of the array (which points at GC memory) from a scannable place to a non-scannable place.
Comment #10 by dfj1esp02 — 2023-08-30T12:35:56Z
As I understand the code initially didn't touch GC (because it can fail after fork or to match posix guarantees) then regressed to use GC.
Comment #11 by schveiguy — 2023-08-30T15:00:47Z
If you look at that commit from 2015, it was using alloca before, and now uses malloc. But the issue is that the `toAStringz` which allocates an array of C strings using the GC. This was the case before the 2015 change, and is still the case now. So the GC was always used.
The issue that happened when moving to malloc is that the GC could clean up those string items before they were sent to execv. When it was using alloca, that went on the stack, and stacks are scanned.
The simple solution is to put that array on the GC.
Given that for at least 8 years, and probably more, using the GC was fine when forking/execing a process, most likely it's either just fine, or it hangs so infrequently that nobody has complained about it.
Using the GC for the string array along with all the strings is at least the same risk as just allocating the strings using the GC.
Comment #12 by kdevel — 2023-10-22T11:43:47Z
(In reply to kdevel from comment #5)
> Created attachment 1868 [details]
> Patch (v0) against process.d
>
> Could not yet test this code. That is how I would implement those D wrappers.
The test is rather simple.
1. Locate process.d (locate src/phobos/std/process.d) and copy it
to the current working directory.
2. Download, compile, execute execv-recur.d (4th attachment)
$ dmd execv-recur.d process.d
$ ./execv-recur 1
[...]
self <./execv-recur> newargs [0, 1] <./execv-recur> <131072>
dump: <2> <4> <2> <32> <2> <4> <2>
[email protected](49): exp <4> actual <131072>
[...]
3. Download the Patch (3rd attachment) apply, recompile, execute:
$ patch -i issue-23654.v0.patch
patching file process.d
Hunk #1 succeeded at 4309 (offset 50 lines).
Hunk #2 succeeded at 4374 (offset 50 lines).
$ dmd execv-recur.d process.d
$ ./execv-recur 1
[...]
self <./execv-recur> newargs [0, 1] <./execv-recur> <262144>
rc = <-1> errno = <7> strerror = <Argument list too long>
This is the expected outcome.
Comment #13 by robert.schadek — 2024-12-01T16:41:03Z