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
224224}
225225
226226fn 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;
228231}
229232
230233fn 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
7979}
8080
8181fn wcslen(str: [*:0]const wchar_t) callconv(.c) usize {
82 return wcsnlen(str, std.math.maxInt(usize));
82 return std.mem.len(str);
8383}
8484
8585fn 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;
8790}
8891
8992fn 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) {
1147111471 .msvc => private._strdup,
1147211472 else => private.strdup,
1147311473};
11474pub extern "c" fn strnlen(s: [*:0]const c_char, n: usize) callconv(.c) usize;
1147411475pub 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;
1147511477pub const wcsdup = switch (builtin.abi) {
1147611478 .msvc => private._wcsdup,
1147711479 else => private.wcsdup,
test/c/string.zig+9
......@@ -50,3 +50,12 @@ test "strndup" {
5050 const copy3_u8: [*:0]u8 = @ptrCast(copy3);
5151 try testing.expectEqualStrings("Hello", std.mem.span(@as([*:0]const u8, copy3_u8)));
5252}
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" {
3232 defer c.free(cpy);
3333 try testing.expectEqual(@as(c.wchar_t, 0), cpy[0]);
3434}
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}