authorgravatar for rpkak@noreply.codeberg.orgrpkak <rpkak@noreply.codeberg.org> 2026-09-17 08:59:38+02:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-09-17 15:35:12+02:00
log8f61a7d34a33c2ec912c3f4404d69586e721c75a
treedee542054a456d4ad6d7d6afbc1c0ef01fcd68e9
parentb0e7ebc24fdbd17f01180450c0e12f6187ef3f27

libzigc: strnlen, wcsnlen: don't read past first zero

Previously is was assumed, that all of str[0..max] is readable.

5 files changed, 29 insertions(+), 3 deletions(-)

lib/c/string.zig+4-1
...@@ -224,7 +224,10 @@ fn stpncpy(noalias dst: [*]c_char, noalias src: [*:0]const c_char, max: usize) c...@@ -224,7 +224,10 @@ fn stpncpy(noalias dst: [*]c_char, noalias src: [*:0]const c_char, max: usize) c
224}224}
225225
226fn strnlen(str: [*:0]const c_char, max: usize) callconv(.c) usize {226fn strnlen(str: [*:0]const c_char, max: usize) callconv(.c) usize {
227 return std.mem.findScalar(u8, @ptrCast(str[0..max]), 0) orelse max;227 for (0..max) |i| {
228 if (str[i] == 0) return i;
229 }
230 return max;
228}231}
229232
230fn memmem(haystack: *const anyopaque, haystack_len: usize, needle: *const anyopaque, needle_len: usize) callconv(.c) ?*anyopaque {233fn memmem(haystack: *const anyopaque, haystack_len: usize, needle: *const anyopaque, needle_len: usize) callconv(.c) ?*anyopaque {
lib/c/wchar.zig+5-2
...@@ -79,11 +79,14 @@ fn wmemset(dest: [*]wchar_t, elem: wchar_t, len: usize) callconv(.c) [*]wchar_t...@@ -79,11 +79,14 @@ fn wmemset(dest: [*]wchar_t, elem: wchar_t, len: usize) callconv(.c) [*]wchar_t
79}79}
8080
81fn wcslen(str: [*:0]const wchar_t) callconv(.c) usize {81fn wcslen(str: [*:0]const wchar_t) callconv(.c) usize {
82 return wcsnlen(str, std.math.maxInt(usize));82 return std.mem.len(str);
83}83}
8484
85fn wcsnlen(str: [*:0]const wchar_t, max: usize) callconv(.c) usize {85fn wcsnlen(str: [*:0]const wchar_t, max: usize) callconv(.c) usize {
86 return std.mem.findScalar(wchar_t, str[0..max], 0) orelse max;86 for (0..max) |i| {
87 if (str[i] == 0) return i;
88 }
89 return max;
87}90}
8891
89fn wcscmp(a: [*:0]const wchar_t, b: [*:0]const wchar_t) callconv(.c) c_int {92fn wcscmp(a: [*:0]const wchar_t, b: [*:0]const wchar_t) callconv(.c) c_int {
lib/std/c.zig+2
...@@ -11471,7 +11471,9 @@ pub const strdup = switch (builtin.abi) {...@@ -11471,7 +11471,9 @@ pub const strdup = switch (builtin.abi) {
11471 .msvc => private._strdup,11471 .msvc => private._strdup,
11472 else => private.strdup,11472 else => private.strdup,
11473};11473};
11474pub extern "c" fn strnlen(s: [*:0]const c_char, n: usize) callconv(.c) usize;
11474pub extern "c" fn strndup(s: [*:0]const c_char, n: usize) ?[*:0]c_char;11475pub extern "c" fn strndup(s: [*:0]const c_char, n: usize) ?[*:0]c_char;
11476pub extern "c" fn wcsnlen(s: [*:0]const wchar_t, n: usize) callconv(.c) usize;
11475pub const wcsdup = switch (builtin.abi) {11477pub const wcsdup = switch (builtin.abi) {
11476 .msvc => private._wcsdup,11478 .msvc => private._wcsdup,
11477 else => private.wcsdup,11479 else => private.wcsdup,
test/c/string.zig+9
...@@ -50,3 +50,12 @@ test "strndup" {...@@ -50,3 +50,12 @@ test "strndup" {
50 const copy3_u8: [*:0]u8 = @ptrCast(copy3);50 const copy3_u8: [*:0]u8 = @ptrCast(copy3);
51 try testing.expectEqualStrings("Hello", std.mem.span(@as([*:0]const u8, copy3_u8)));51 try testing.expectEqualStrings("Hello", std.mem.span(@as([*:0]const u8, copy3_u8)));
52}52}
53
54test "strnlen" {
55 const str: [*:0]const u8 = "Hello";
56 try testing.expectEqual(3, c.strnlen(@ptrCast(str), 3));
57 try testing.expectEqual(4, c.strnlen(@ptrCast(str), 4));
58 try testing.expectEqual(5, c.strnlen(@ptrCast(str), 5));
59 try testing.expectEqual(5, c.strnlen(@ptrCast(str), 6));
60 try testing.expectEqual(5, c.strnlen(@ptrCast(str), 7));
61}
test/c/wchar.zig+9
...@@ -32,3 +32,12 @@ test "wcsdup empty string" {...@@ -32,3 +32,12 @@ test "wcsdup empty string" {
32 defer c.free(cpy);32 defer c.free(cpy);
33 try testing.expectEqual(@as(c.wchar_t, 0), cpy[0]);33 try testing.expectEqual(@as(c.wchar_t, 0), cpy[0]);
34}34}
35
36test "wcsnlen" {
37 const wcs: [*:0]const c.wchar_t = &.{ 'H', 'e', 'l', 'l', 'o' };
38 try testing.expectEqual(3, c.wcsnlen(wcs, 3));
39 try testing.expectEqual(4, c.wcsnlen(wcs, 4));
40 try testing.expectEqual(5, c.wcsnlen(wcs, 5));
41 try testing.expectEqual(5, c.wcsnlen(wcs, 6));
42 try testing.expectEqual(5, c.wcsnlen(wcs, 7));
43}