Skip to content

Commit cbc27af

Browse files
committed
Sweep emulator workaround bugs
1 parent 837a2b9 commit cbc27af

10 files changed

Lines changed: 347 additions & 68 deletions

File tree

build.zig

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -276,22 +276,23 @@ pub fn build(b: *std.Build) void {
276276
run_step.addCheck(.{ .expect_stdout_exact = "Success!\n" });
277277
test_step.dependOn(&run_step.step);
278278
}
279+
{
280+
const exe = addTest("startup_extensive", b, target, optimize, libc_only_std_static, zig_start);
281+
const run_step = addRunArtifactCompat(b, exe);
282+
run_step.addCheck(.{ .expect_stdout_exact = "Success!\n" });
283+
test_step.dependOn(&run_step.step);
284+
}
279285
{
280286
const exe = addTest("gnu_extensive", b, target, optimize, libc_only_std_static, zig_start);
281287
exe.addIncludePath(lazyPath(b, "inc" ++ std.fs.path.sep_str ++ "gnu"));
282288
exe.linkLibrary(libc_only_gnu);
283289
addPosix(exe, libc_only_posix);
284-
if (externalRunnerFor(exe) != .darling) {
285-
const run_step = addRunArtifactCompat(b, test_env_exe);
286-
addArtifactArgCompat(run_step, b, exe);
287-
configureExternalHelperRunner(run_step, exe);
288-
run_step.addCheck(.{ .expect_stdout_exact = "Success!\n" });
289-
test_step.dependOn(&run_step.step);
290-
}
291-
// Darling still corrupts argv during the GNU long-option/argp path for
292-
// this binary specifically. Keep native macOS covering the surface; the
293-
// emulator is useful for the broader POSIX/socket/pthread regressions,
294-
// but not as a trustworthy signal for this argv-heavy GNU test.
290+
const run_step = addRunArtifactCompat(b, test_env_exe);
291+
addArtifactArgCompat(run_step, b, exe);
292+
configureExternalHelperRunner(run_step, exe);
293+
run_step.setEnvironmentVariable("ZIGLIBC_TEST_MARKERS", "1");
294+
run_step.addCheck(.{ .expect_stdout_exact = "Success!\n" });
295+
test_step.dependOn(&run_step.step);
295296
}
296297
{
297298
const exe = addTest("socket_extensive", b, target, optimize, libc_only_std_static, zig_start);
@@ -300,6 +301,18 @@ pub fn build(b: *std.Build) void {
300301
run_step.addCheck(.{ .expect_stdout_exact = "Success!\n" });
301302
test_step.dependOn(&run_step.step);
302303
}
304+
{
305+
const exe = addTest("string_extensive", b, target, optimize, libc_only_std_static, zig_start);
306+
const run_step = addRunArtifactCompat(b, exe);
307+
run_step.addCheck(.{ .expect_stdout_exact = "Success!\n" });
308+
test_step.dependOn(&run_step.step);
309+
}
310+
{
311+
const exe = addTest("argv_extensive", b, target, optimize, libc_only_std_static, zig_start);
312+
const run_step = addRunArtifactCompat(b, exe);
313+
run_step.addCheck(.{ .expect_stdout_exact = "Success!\n" });
314+
test_step.dependOn(&run_step.step);
315+
}
303316
{
304317
const exe = addExecutableCompat(b, .{
305318
.name = "pthread_extensive",
@@ -836,13 +849,16 @@ fn addLibcTest(
836849
exe.linkSystemLibrary("kernel32");
837850
exe.linkSystemLibrary("ws2_32");
838851
}
839-
if (!(externalRunnerFor(exe) == .darling and std.mem.eql(u8, name, "string"))) {
852+
if (!(externalRunnerFor(exe) == .darling and
853+
(std.mem.eql(u8, name, "argv") or std.mem.eql(u8, name, "string"))))
854+
{
840855
libc_test_step.dependOn(&addRunArtifactCompat(b, exe).step);
841856
}
842-
// Darling now runs the other libc-test functional binaries reliably, but
843-
// `functional/string.c` still aborts under the emulator before any
844-
// diagnostic output. Keep the gate narrow so Darwin-target conformance
845-
// coverage keeps expanding without teaching the libc about emulator quirks.
857+
// Darling still misbehaves on the external libc-test `argv`/`string`
858+
// binaries even though the same libc surfaces are covered by our own
859+
// Darwin-target `argv_extensive` and `string_extensive` tests in the
860+
// normal `zig build test` matrix. Keep the emulator-specific gate narrow
861+
// and local to those vendored binaries only.
846862
}
847863
return libc_test_step;
848864
}

src/cstd.zig

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -855,16 +855,23 @@ export fn strncpy(s1: [*]u8, s2: [*:0]const u8, n: usize) callconv(.c) [*]u8 {
855855
// they don't appear to be a part of any standard.
856856
// not sure whether they should live in this library or a separate one
857857
// see https://lwn.net/Articles/507319/
858-
export fn strlcpy(dst: [*]u8, src: [*:0]const u8, size: usize) callconv(.c) usize {
858+
export fn strlcpy(dst: ?[*]u8, src: [*:0]const u8, size: usize) callconv(.c) usize {
859859
trace.log("strncpy {*} {*} n={}", .{ dst, src, size });
860+
// C callers may pass a null destination when `size == 0`; that is valid as
861+
// long as the function only reports the source length and never dereferences
862+
// `dst`. Using a non-null Zig pointer here lets the backend assume the
863+
// pointer is always valid, which can turn this libc edge case into
864+
// target-specific crashes that only show up on some ABIs/backends.
865+
if (size == 0) return strlen(src);
866+
const out = dst.?;
860867
var i: usize = 0;
861868
while (true) : (i += 1) {
862869
if (i == size) {
863870
if (size > 0)
864-
dst[size - 1] = 0;
871+
out[size - 1] = 0;
865872
return i + strlen(src + i);
866873
}
867-
dst[i] = src[i];
874+
out[i] = src[i];
868875
if (src[i] == 0) {
869876
return i;
870877
}

test/argv_extensive.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#include <limits.h>
2+
#include <stdio.h>
3+
4+
static int fail(const char *name)
5+
{
6+
fputs(name, stderr);
7+
fputc('\n', stderr);
8+
return 1;
9+
}
10+
11+
int main(int argc, char **argv)
12+
{
13+
char buf[PATH_MAX];
14+
int n;
15+
16+
if (argc != 1) return fail("argc");
17+
if (argv == NULL) return fail("argv-null");
18+
if (argv[0] == NULL) return fail("argv0-null");
19+
if (argv[1] != NULL) return fail("argv1-nonnull");
20+
if (argv[0][0] == '\0') return fail("argv0-empty");
21+
n = snprintf(buf, sizeof buf, "%s", argv[0]);
22+
if (n < 0) return fail("snprintf-neg");
23+
if (n >= (int)sizeof(buf)) return fail("snprintf-path");
24+
25+
puts("Success!");
26+
return 0;
27+
}

test/gnu_extensive.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,21 @@
33
#include <getopt.h>
44
#include <glob.h>
55
#include <stdio.h>
6+
#include <stdlib.h>
67
#include <string.h>
78

89
#include "expect.h"
910

10-
#define GNU_MARK(label) do { } while (0)
11+
static void gnu_mark(const char *label)
12+
{
13+
if (getenv("ZIGLIBC_TEST_MARKERS") != NULL) {
14+
fputs(label, stderr);
15+
fputc('\n', stderr);
16+
fflush(stderr);
17+
}
18+
}
19+
20+
#define GNU_MARK(label) gnu_mark(label)
1121

1222
static int saw_args;
1323
static int saw_no_args;

test/libc_parity.c

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,15 @@ static void parity_handler(int sig)
4949
(void)sig;
5050
}
5151

52+
static void parity_mark(const char *name)
53+
{
54+
if (getenv("ZIGLIBC_TEST_MARKERS") != NULL) {
55+
fputs(name, stderr);
56+
fputc('\n', stderr);
57+
fflush(stderr);
58+
}
59+
}
60+
5261
static void read_all(FILE *stream, char *buf, size_t len)
5362
{
5463
size_t n = fread(buf, 1, len - 1, stream);
@@ -121,25 +130,34 @@ int main(void)
121130
}
122131

123132
{
133+
parity_mark("parity:block:signal");
124134
void (*old_handler)(int) = signal(SIGINT, parity_handler);
125135
void (*prev_handler)(int) = signal(SIGINT, old_handler);
126136
printf("signal-basic:%d:%d|", old_handler != SIG_ERR, prev_handler == parity_handler);
127137
}
128138

129139
{
140+
parity_mark("parity:block:strtol");
130141
const char *s = "abc";
131142
char *endptr = NULL;
143+
long value;
144+
long dist;
132145
errno = 123;
133-
printf("strtol-nodigits:%ld:%d:%ld|", strtol(s, &endptr, 10), errno, (long)(endptr - s));
146+
value = strtol(s, &endptr, 10);
147+
dist = (long)(endptr - s);
148+
printf("strtol-nodigits:%ld:%d:%ld|", value, errno, dist);
134149
}
135150

136151
{
152+
parity_mark("parity:block:strtod");
137153
const char *s = "abc";
138154
char *endptr = NULL;
139155
double value;
156+
long dist;
140157
errno = 123;
141158
value = strtod(s, &endptr);
142-
printf("strtod-nodigits:%d:%d:%ld|", value == 0.0, errno, (long)(endptr - s));
159+
dist = (long)(endptr - s);
160+
printf("strtod-nodigits:%d:%d:%ld|", value == 0.0, errno, dist);
143161
}
144162

145163
#if LIBC_PARITY_HAVE_STRSIGNAL

test/startup_extensive.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#include <limits.h>
2+
#include <stdio.h>
3+
4+
#include "expect.h"
5+
6+
int main(int argc, char **argv)
7+
{
8+
char buf[PATH_MAX];
9+
int n = snprintf(buf, sizeof(buf), "%s", argv[0]);
10+
if (argc != 1) {
11+
fputs("startup:argc!=1\n", stderr);
12+
return 1;
13+
}
14+
if (argv == NULL) {
15+
fputs("startup:argv-null\n", stderr);
16+
return 1;
17+
}
18+
if (argv[0] == NULL) {
19+
fputs("startup:argv0-null\n", stderr);
20+
return 1;
21+
}
22+
if (argv[1] != NULL) {
23+
fputs("startup:argv1-not-null\n", stderr);
24+
return 1;
25+
}
26+
if (argv[0][0] == '\0') {
27+
fputs("startup:argv0-empty\n", stderr);
28+
return 1;
29+
}
30+
if (n >= (int)sizeof(buf)) {
31+
fputs("startup:argv0-not-valid-path\n", stderr);
32+
return 1;
33+
}
34+
35+
puts("Success!");
36+
return 0;
37+
}

test/string_extensive.c

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
#include <stdio.h>
2+
#include <string.h>
3+
4+
static int fail(const char *name)
5+
{
6+
fputs(name, stderr);
7+
fputc('\n', stderr);
8+
return 1;
9+
}
10+
11+
int main(void)
12+
{
13+
char b[32];
14+
char *s;
15+
int i;
16+
17+
b[16] = 'a';
18+
b[17] = 'b';
19+
b[18] = 'c';
20+
b[19] = 0;
21+
s = strcpy(b, b + 16);
22+
if (s != b) return fail("strcpy-ret-0");
23+
if (strcmp(s, "abc") != 0) return fail("strcpy-val-0");
24+
s = strcpy(b + 1, b + 16);
25+
if (s != b + 1) return fail("strcpy-ret-1");
26+
if (strcmp(s, "abc") != 0) return fail("strcpy-val-1");
27+
s = strcpy(b + 2, b + 18);
28+
if (s != b + 2) return fail("strcpy-ret-2");
29+
if (strcmp(s, "c") != 0) return fail("strcpy-val-2");
30+
31+
s = memset(b, 'x', sizeof b);
32+
if (s != b) return fail("memset-ret");
33+
s = strncpy(b, "abc", sizeof b - 1);
34+
if (s != b) return fail("strncpy-ret");
35+
if (memcmp(b, "abc\0\0\0\0", 8) != 0) return fail("strncpy-zeropad");
36+
if (b[sizeof b - 1] != 'x') return fail("strncpy-overrun");
37+
38+
b[3] = 'x';
39+
b[4] = 0;
40+
strncpy(b, "abc", 3);
41+
if (b[2] != 'c') return fail("strncpy-last");
42+
if (b[3] != 'x') return fail("strncpy-nullterm");
43+
44+
if (!strncmp("abcd", "abce", 3)) {
45+
} else return fail("strncmp-n");
46+
if (!!strncmp("abc", "abd", 3) != 1) return fail("strncmp-byte");
47+
48+
strcpy(b, "abc");
49+
s = strncat(b, "123456", 3);
50+
if (s != b) return fail("strncat-ret");
51+
if (b[6] != 0) return fail("strncat-null");
52+
if (strcmp(s, "abc123") != 0) return fail("strncat-val");
53+
54+
strcpy(b, "aaababccdd0001122223");
55+
if (strchr(b, 'b') != b + 3) return fail("strchr");
56+
if (strrchr(b, 'b') != b + 5) return fail("strrchr");
57+
if (strspn(b, "abcd") != 10) return fail("strspn");
58+
if (strcspn(b, "0123") != 10) return fail("strcspn");
59+
if (strpbrk(b, "0123") != b + 10) return fail("strpbrk");
60+
61+
strcpy(b, "abc 123; xyz; foo");
62+
s = strtok(b, " ");
63+
if (s != b) return fail("strtok-0-ret");
64+
if (strcmp(s, "abc") != 0) return fail("strtok-0-val");
65+
s = strtok(NULL, ";");
66+
if (s != b + 4) return fail("strtok-1-ret");
67+
if (strcmp(s, " 123") != 0) return fail("strtok-1-val");
68+
s = strtok(NULL, " ;");
69+
if (s != b + 11) return fail("strtok-2-ret");
70+
if (strcmp(s, "xyz") != 0) return fail("strtok-2-val");
71+
s = strtok(NULL, " ;");
72+
if (s != b + 16) return fail("strtok-3-ret");
73+
if (strcmp(s, "foo") != 0) return fail("strtok-3-val");
74+
75+
memset(b, 'x', sizeof b);
76+
if (strlcpy(b, "abc", sizeof b - 1) != 3) return fail("strlcpy-len-0");
77+
if (b[3] != 0) return fail("strlcpy-null-0");
78+
if (b[4] != 'x') return fail("strlcpy-extra-0");
79+
80+
memset(b, 'x', sizeof b);
81+
if (strlcpy(b, "abc", 2) != 3) return fail("strlcpy-len-1");
82+
if (b[0] != 'a') return fail("strlcpy-copy-1");
83+
if (b[1] != 0) return fail("strlcpy-null-1");
84+
85+
memset(b, 'x', sizeof b);
86+
if (strlcpy(b, "abc", 3) != 3) return fail("strlcpy-len-2");
87+
if (b[2] != 0) return fail("strlcpy-null-2");
88+
if (strlcpy(NULL, "abc", 0) != 3) return fail("strlcpy-null-size0");
89+
90+
memcpy(b, "abc\0\0\0x\0", 8);
91+
if (strlcat(b, "123", sizeof b) != 6) return fail("strlcat-len-0");
92+
if (strcmp(b, "abc123") != 0) return fail("strlcat-val-0");
93+
94+
memcpy(b, "abc\0\0\0x\0", 8);
95+
if (strlcat(b, "123", 6) != 6) return fail("strlcat-len-1");
96+
if (strcmp(b, "abc12") != 0) return fail("strlcat-val-1");
97+
if (b[6] != 'x') return fail("strlcat-overrun");
98+
99+
memcpy(b, "abc\0\0\0x\0", 8);
100+
if (strlcat(b, "123", 4) != 6) return fail("strlcat-len-2");
101+
if (strcmp(b, "abc") != 0) return fail("strlcat-val-2");
102+
103+
memcpy(b, "abc\0\0\0x\0", 8);
104+
if (strlcat(b, "123", 3) != 6) return fail("strlcat-len-3");
105+
if (strcmp(b, "abc") != 0) return fail("strlcat-val-3");
106+
107+
puts("Success!");
108+
return 0;
109+
}

test/strings.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,15 @@ int main(int argc, char *argv[])
7777
expect(0 == strcmp(dst, "hello world"));
7878
}
7979

80+
{
81+
char dst[8];
82+
expect(3 == strlcpy(NULL, "abc", 0));
83+
expect(3 == strlcpy(dst, "abc", sizeof(dst)));
84+
expect(0 == strcmp(dst, "abc"));
85+
expect(6 == strlcat(dst, "123", sizeof(dst)));
86+
expect(0 == strcmp(dst, "abc123"));
87+
}
88+
8089
puts("Success!");
8190
return 0;
8291
}

0 commit comments

Comments
 (0)