-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
std.process.Child.run failing depending on Release options #21756
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
What windows version (and CPU architecture) did you test this on? I ran the test on Windows 11 Home Version 21H2. Can you maybe check with a newer nightly build whether other configurations still fail on your system? |
I'm running Windows 11 home Version 23H2 on a 64-bit operating system, x64-based processor. I'll upgrade to a newer build and run the same tests. I will edit this message when I do. Edit: This is my first bug report, so I don't really know what to do now. Please advise. |
I can also reproduce the Both the EDIT: Compiling with > zig run 21756.zig -OReleaseSmall -ferror-tracing -fno-strip
error:
C:\Users\Ryan\Programming\Zig\zig\lib\std\start.zig:439:53: 0xfc11f5 in WinStartup (21756.exe.obj)
std.os.windows.ntdll.RtlExitUserProcess(callMain());
^
C:\Users\Ryan\Programming\Zig\zig\lib\std\start.zig:439:53: 0xfc120d in WinStartup (21756.exe.obj)
std.os.windows.ntdll.RtlExitUserProcess(callMain());
^
C:\Users\Ryan\Programming\Zig\zig\lib\std\start.zig:439:53: 0xfc1215 in WinStartup (21756.exe.obj)
std.os.windows.ntdll.RtlExitUserProcess(callMain());
^ My first thought is that there's a miscompilation involved. |
It's the bare minimum error trace, basically saying that it was I did some more experimenting using print debugging and narrowed down the issue to this function: // ... std.process.Child.collectOutput
assert(child.stdout_behavior == .Pipe);
assert(child.stderr_behavior == .Pipe);
// my changes
std.debug.print("trying to assert that allocators are the same\n", .{});
// end my changes
// we could make this work with multiple allocators but YAGNI
if (stdout.allocator.ptr != stderr.allocator.ptr or
stdout.allocator.vtable != stderr.allocator.vtable)
{
// my changes
std.debug.print("assertion failed, unreachable code reached\n", .{});
// end my changes
unreachable; // ChildProcess.collectOutput only supports 1 allocator
}
// my changes
std.debug.print("assertion passed, continuing...\n", .{});
// end my changes
var poller = std.io.poll(stdout.allocator, enum { stdout, stderr }, .{
.stdout = child.stdout.?,
.stderr = child.stderr.?,
});
// ... std.process.Child.collectOutput For some reason, this program prints "trying to assert that allocators are the same" and then exits, without printing "assertion failed, unreachable code reached". I really don't know what to make of this, since it seems the |
Some more strange info:
My naive guess is one of:
I don't have enough experience with these types of bugs to be able to differentiate between those possibilities, though. |
For print debugging in general, I don't think that a branch that includes Update: Update2: Since the ptr value of |
I think you figured it out. Setting Lines 278 to 279 in 9f84f7f
Maybe something like: @ptrFromInt(std.mem.alignBackward(usize, std.math.maxInt(usize), @alignOf(*anyopaque))) |
Related other issues:
Basically, the definition intends to express an |
Note that there might be more going on than just invalid bit patterns, since if I add
Isn't the comparison of EDIT:
Yeah, seems like the bug is either the |
The use of undefined here meant that it was always unsafe/illegal to compare the ptr fields of Allocator/Random. However, this restriction is not mentioned anywhere and the ptr field *is* being compared in real code, e.g. `std.process.Child.collectOutput`, which can lead to undefined behavior in release modes (see ziglang#21756). There are two ways to address this: 1. Codify that comparing `ptr` is always illegal/unsafe, and remove all existing comparisons. 2. Set `ptr` to a legal dummy value, similar to the value returned by Allocator.alloc when a zero-length slice is requested. The first option seems like footgun central (at least until usage of `undefined` is fully safety-checked in safe modes), so I went with the second option. Closes ziglang#21756
The use of undefined here meant that it was always unsafe/illegal to compare the ptr fields of Allocator/Random. However, this restriction is not mentioned anywhere and the ptr field *is* being compared in real code, e.g. `std.process.Child.collectOutput`, which can lead to undefined behavior in release modes (see ziglang#21756). There are two ways to address this: 1. Codify that comparing `ptr` is always illegal/unsafe, and remove all existing comparisons. 2. Set `ptr` to a legal dummy value, similar to the value returned by Allocator.alloc when a zero-length slice is requested. The first option seems like footgun central (at least until usage of `undefined` is fully safety-checked in safe modes), so I went with the second option. Closes ziglang#21756
… instances Previously, Allocator implementations that did not store any state would set `ptr` to `undefined`. However, the use of undefined meant that it was always unsafe/illegal to compare the ptr fields of Allocator/Random. This restriction was not mentioned anywhere, though, and the ptr field *was* being compared in real code, e.g. `std.process.Child.collectOutput`, which can lead to undefined behavior in release modes (see ziglang#21756). There are a few ways to address this: 1. Codify that comparing `ptr` is always illegal/unsafe, and remove all existing comparisons. 2. Set `ptr` to a legal dummy value, similar to the value returned by Allocator.alloc when a zero-length slice is requested. 3. Make `ptr` optional and set it to `null` for implementations that don't store any state The first option seems like footgun central (at least until usage of `undefined` is fully safety-checked in safe modes), and the second option has its own issues since the dummy value could inadvertently be equal to a real mapped pointer, so the 3rd option was chosen.
… instances Previously, Allocator implementations that did not store any state would set `ptr` to `undefined`. However, the use of undefined meant that it was always unsafe/illegal to compare the ptr fields of Allocator. This restriction was not mentioned anywhere, though, and the ptr field *was* being compared in real code, e.g. `std.process.Child.collectOutput`, which can lead to undefined behavior in release modes (see ziglang#21756). There are a few ways to address this: 1. Codify that comparing `ptr` is always illegal/unsafe, and remove all existing comparisons. 2. Set `ptr` to a legal dummy value, similar to the value returned by Allocator.alloc when a zero-length slice is requested. 3. Make `ptr` optional and set it to `null` for implementations that don't store any state The first option seems like footgun central (at least until usage of `undefined` is fully safety-checked in safe modes), and the second option has its own issues since the dummy value could inadvertently be equal to a real mapped pointer, so the 3rd option was chosen.
… instances Previously, Allocator implementations that did not store any state would set `ptr` to `undefined`. However, the use of undefined meant that it was always unsafe/illegal to compare the ptr fields of Allocator. This restriction was not mentioned anywhere, though, and the ptr field *was* being compared in real code, e.g. `std.process.Child.collectOutput`, which can lead to undefined behavior in release modes (see ziglang#21756). There are a few ways to address this: 1. Codify that comparing `ptr` is always illegal/unsafe, and remove all existing comparisons. 2. Set `ptr` to a legal dummy value, similar to the value returned by Allocator.alloc when a zero-length slice is requested. 3. Make `ptr` optional and set it to `null` for implementations that don't store any state The first option seems like footgun central (at least until usage of `undefined` is fully safety-checked in safe modes), and the second option has its own issues since the dummy value could inadvertently be equal to a real mapped pointer, so the 3rd option was chosen.
… instances Previously, Allocator implementations that did not store any state would set `ptr` to `undefined`. However, the use of undefined meant that it was always unsafe/illegal to compare the ptr fields of Allocator. This restriction was not mentioned anywhere, though, and the ptr field *was* being compared in real code, e.g. `std.process.Child.collectOutput`, which can lead to undefined behavior in release modes (see ziglang#21756). There are a few ways to address this: 1. Codify that comparing `ptr` is always illegal/unsafe, and remove all existing comparisons. 2. Set `ptr` to a legal dummy value, similar to the value returned by Allocator.alloc when a zero-length slice is requested. 3. Make `ptr` optional and set it to `null` for implementations that don't store any state The first option seems like footgun central (at least until usage of `undefined` is fully safety-checked in safe modes), and the second option has its own issues since the dummy value could inadvertently be equal to a real mapped pointer, so the 3rd option was chosen.
Zig Version
0.14.0-dev.719+f21928657
Steps to Reproduce and Observed Behavior
Create a file
bug.zig
and paste the following code:Commands and output are listed below:
Expected Behavior
Identical output on all release modes on which should be the
zig version
:0.14.0-dev.719+f21928657
.The text was updated successfully, but these errors were encountered: