diff options
| author | LemonBoy <thatlemon@gmail.com> | 2020-10-02 16:48:18 +0200 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2020-10-16 21:22:14 -0400 |
| commit | d44486b274a916823fcf6045a6400ef51e07d544 (patch) | |
| tree | c12a3a3923b644d219f815630941073d99d31889 /lib/std/special | |
| parent | 2aff27d92247eed3b64ffc68da8fae9e5994ea0b (diff) | |
| download | zig-d44486b274a916823fcf6045a6400ef51e07d544.tar.gz zig-d44486b274a916823fcf6045a6400ef51e07d544.zip | |
std: Add libssp implementation for GNU/Windows targets
Unlike glibc and musl, MinGW provides no libssp symbols leading to
countless compile errors if FORTIFY_SOURCE is defined.
Add a (incomplete) implementation of libssp written in Zig so that
linking succeeds.
Closes #6492
Diffstat (limited to 'lib/std/special')
| -rw-r--r-- | lib/std/special/c.zig | 146 | ||||
| -rw-r--r-- | lib/std/special/compiler_rt.zig | 19 | ||||
| -rw-r--r-- | lib/std/special/ssp.zig | 141 |
3 files changed, 254 insertions, 52 deletions
diff --git a/lib/std/special/c.zig b/lib/std/special/c.zig index 53f7e1738d..58b48f1607 100644 --- a/lib/std/special/c.zig +++ b/lib/std/special/c.zig @@ -35,6 +35,10 @@ comptime { @export(strncmp, .{ .name = "strncmp", .linkage = .Strong }); @export(strerror, .{ .name = "strerror", .linkage = .Strong }); @export(strlen, .{ .name = "strlen", .linkage = .Strong }); + @export(strcpy, .{ .name = "strcpy", .linkage = .Strong }); + @export(strncpy, .{ .name = "strncpy", .linkage = .Strong }); + @export(strcat, .{ .name = "strcat", .linkage = .Strong }); + @export(strncat, .{ .name = "strncat", .linkage = .Strong }); } else if (is_msvc) { @export(_fltused, .{ .name = "_fltused", .linkage = .Strong }); } @@ -47,6 +51,90 @@ fn wasm_start() callconv(.C) void { _ = main(0, undefined); } +fn strcpy(dest: [*:0]u8, src: [*:0]const u8) callconv(.C) [*:0]u8 { + var i: usize = 0; + while (src[i] != 0) : (i += 1) { + dest[i] = src[i]; + } + dest[i] = 0; + + return dest; +} + +test "strcpy" { + var s1: [9:0]u8 = undefined; + + s1[0] = 0; + _ = strcpy(&s1, "foobarbaz"); + std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.spanZ(&s1)); +} + +fn strncpy(dest: [*:0]u8, src: [*:0]const u8, n: usize) callconv(.C) [*:0]u8 { + var i: usize = 0; + while (i < n and src[i] != 0) : (i += 1) { + dest[i] = src[i]; + } + while (i < n) : (i += 1) { + dest[i] = 0; + } + + return dest; +} + +test "strncpy" { + var s1: [9:0]u8 = undefined; + + s1[0] = 0; + _ = strncpy(&s1, "foobarbaz", 9); + std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.spanZ(&s1)); +} + +fn strcat(dest: [*:0]u8, src: [*:0]const u8) callconv(.C) [*:0]u8 { + var dest_end: usize = 0; + while (dest[dest_end] != 0) : (dest_end += 1) {} + + var i: usize = 0; + while (src[i] != 0) : (i += 1) { + dest[dest_end + i] = src[i]; + } + dest[dest_end + i] = 0; + + return dest; +} + +test "strcat" { + var s1: [9:0]u8 = undefined; + + s1[0] = 0; + _ = strcat(&s1, "foo"); + _ = strcat(&s1, "bar"); + _ = strcat(&s1, "baz"); + std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.spanZ(&s1)); +} + +fn strncat(dest: [*:0]u8, src: [*:0]const u8, avail: usize) callconv(.C) [*:0]u8 { + var dest_end: usize = 0; + while (dest[dest_end] != 0) : (dest_end += 1) {} + + var i: usize = 0; + while (i < avail and src[i] != 0) : (i += 1) { + dest[dest_end + i] = src[i]; + } + dest[dest_end + i] = 0; + + return dest; +} + +test "strncat" { + var s1: [9:0]u8 = undefined; + + s1[0] = 0; + _ = strncat(&s1, "foo1111", 3); + _ = strncat(&s1, "bar1111", 3); + _ = strncat(&s1, "baz1111", 3); + std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.spanZ(&s1)); +} + fn strcmp(s1: [*:0]const u8, s2: [*:0]const u8) callconv(.C) c_int { return std.cstr.cmp(s1, s2); } @@ -92,7 +180,7 @@ pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn while (true) {} } -export fn memset(dest: ?[*]u8, c: u8, n: usize) ?[*]u8 { +export fn memset(dest: ?[*]u8, c: u8, n: usize) callconv(.C) ?[*]u8 { @setRuntimeSafety(false); var index: usize = 0; @@ -102,7 +190,13 @@ export fn memset(dest: ?[*]u8, c: u8, n: usize) ?[*]u8 { return dest; } -export fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize) ?[*]u8 { +export fn __memset(dest: ?[*]u8, c: u8, n: usize, dest_n: usize) callconv(.C) ?[*]u8 { + if (dest_n < n) + @panic("buffer overflow"); + return memset(dest, c, n); +} + +export fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize) callconv(.C) ?[*]u8 { @setRuntimeSafety(false); var index: usize = 0; @@ -112,7 +206,7 @@ export fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize) ?[*] return dest; } -export fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) ?[*]u8 { +export fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) callconv(.C) ?[*]u8 { @setRuntimeSafety(false); if (@ptrToInt(dest) < @ptrToInt(src)) { @@ -131,7 +225,7 @@ export fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) ?[*]u8 { return dest; } -export fn memcmp(vl: ?[*]const u8, vr: ?[*]const u8, n: usize) isize { +export fn memcmp(vl: ?[*]const u8, vr: ?[*]const u8, n: usize) callconv(.C) isize { @setRuntimeSafety(false); var index: usize = 0; @@ -146,17 +240,17 @@ export fn memcmp(vl: ?[*]const u8, vr: ?[*]const u8, n: usize) isize { } test "test_memcmp" { - const base_arr = []u8{ 1, 1, 1 }; - const arr1 = []u8{ 1, 1, 1 }; - const arr2 = []u8{ 1, 0, 1 }; - const arr3 = []u8{ 1, 2, 1 }; + const base_arr = &[_]u8{ 1, 1, 1 }; + const arr1 = &[_]u8{ 1, 1, 1 }; + const arr2 = &[_]u8{ 1, 0, 1 }; + const arr3 = &[_]u8{ 1, 2, 1 }; - std.testing.expect(memcmp(base_arr[0..].ptr, arr1[0..].ptr, base_arr.len) == 0); - std.testing.expect(memcmp(base_arr[0..].ptr, arr2[0..].ptr, base_arr.len) > 0); - std.testing.expect(memcmp(base_arr[0..].ptr, arr3[0..].ptr, base_arr.len) < 0); + std.testing.expect(memcmp(base_arr[0..], arr1[0..], base_arr.len) == 0); + std.testing.expect(memcmp(base_arr[0..], arr2[0..], base_arr.len) > 0); + std.testing.expect(memcmp(base_arr[0..], arr3[0..], base_arr.len) < 0); } -export fn bcmp(vl: [*]allowzero const u8, vr: [*]allowzero const u8, n: usize) isize { +export fn bcmp(vl: [*]allowzero const u8, vr: [*]allowzero const u8, n: usize) callconv(.C) isize { @setRuntimeSafety(false); var index: usize = 0; @@ -170,30 +264,21 @@ export fn bcmp(vl: [*]allowzero const u8, vr: [*]allowzero const u8, n: usize) i } test "test_bcmp" { - const base_arr = []u8{ 1, 1, 1 }; - const arr1 = []u8{ 1, 1, 1 }; - const arr2 = []u8{ 1, 0, 1 }; - const arr3 = []u8{ 1, 2, 1 }; + const base_arr = &[_]u8{ 1, 1, 1 }; + const arr1 = &[_]u8{ 1, 1, 1 }; + const arr2 = &[_]u8{ 1, 0, 1 }; + const arr3 = &[_]u8{ 1, 2, 1 }; - std.testing.expect(bcmp(base_arr[0..].ptr, arr1[0..].ptr, base_arr.len) == 0); - std.testing.expect(bcmp(base_arr[0..].ptr, arr2[0..].ptr, base_arr.len) != 0); - std.testing.expect(bcmp(base_arr[0..].ptr, arr3[0..].ptr, base_arr.len) != 0); + std.testing.expect(bcmp(base_arr[0..], arr1[0..], base_arr.len) == 0); + std.testing.expect(bcmp(base_arr[0..], arr2[0..], base_arr.len) != 0); + std.testing.expect(bcmp(base_arr[0..], arr3[0..], base_arr.len) != 0); } comptime { - if (builtin.mode != builtin.Mode.ReleaseFast and - builtin.mode != builtin.Mode.ReleaseSmall and - builtin.os.tag != .windows) - { - @export(__stack_chk_fail, .{ .name = "__stack_chk_fail" }); - } if (builtin.os.tag == .linux) { @export(clone, .{ .name = "clone" }); } } -fn __stack_chk_fail() callconv(.C) noreturn { - @panic("stack smashing detected"); -} // TODO we should be able to put this directly in std/linux/x86_64.zig but // it causes a segfault in release mode. this is a workaround of calling it @@ -416,7 +501,6 @@ fn clone() callconv(.Naked) void { \\ # move syscall number into r0 \\ li 0, 120 \\ sc - \\ # check for syscall error \\ bns+ 1f # jump to label 1 if no summary overflow. \\ #else @@ -424,10 +508,8 @@ fn clone() callconv(.Naked) void { \\1: \\ # compare sc result with 0 \\ cmpwi cr7, 3, 0 - \\ # if not 0, jump to end \\ bne cr7, 2f - \\ #else: we're the child \\ #call funcptr: move arg (d) into r3 \\ mr 3, 31 @@ -438,13 +520,11 @@ fn clone() callconv(.Naked) void { \\ # mov SYS_exit into r0 (the exit param is already in r3) \\ li 0, 1 \\ sc - \\2: \\ # restore stack \\ lwz 30, 0(1) \\ lwz 31, 4(1) \\ addi 1, 1, 16 - \\ blr ); }, diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index e2105f6084..98d292cce9 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -284,11 +284,6 @@ comptime { @export(@import("compiler_rt/stack_probe.zig").__chkstk, .{ .name = "__chkstk", .linkage = strong_linkage }); } - if (is_mingw) { - @export(__stack_chk_fail, .{ .name = "__stack_chk_fail", .linkage = strong_linkage }); - @export(__stack_chk_guard, .{ .name = "__stack_chk_guard", .linkage = strong_linkage }); - } - switch (builtin.arch) { .i386 => { @export(@import("compiler_rt/divti3.zig").__divti3, .{ .name = "__divti3", .linkage = linkage }); @@ -311,9 +306,6 @@ comptime { else => {}, } } else { - if (std.Target.current.isGnuLibC() and builtin.link_libc) { - @export(__stack_chk_guard, .{ .name = "__stack_chk_guard", .linkage = linkage }); - } @export(@import("compiler_rt/divti3.zig").__divti3, .{ .name = "__divti3", .linkage = linkage }); @export(@import("compiler_rt/modti3.zig").__modti3, .{ .name = "__modti3", .linkage = linkage }); @export(@import("compiler_rt/multi3.zig").__multi3, .{ .name = "__multi3", .linkage = linkage }); @@ -337,14 +329,3 @@ pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn unreachable; } } - -fn __stack_chk_fail() callconv(.C) noreturn { - @panic("stack smashing detected"); -} - -var __stack_chk_guard: usize = blk: { - var buf = [1]u8{0} ** @sizeOf(usize); - buf[@sizeOf(usize) - 1] = 255; - buf[@sizeOf(usize) - 2] = '\n'; - break :blk @bitCast(usize, buf); -}; diff --git a/lib/std/special/ssp.zig b/lib/std/special/ssp.zig new file mode 100644 index 0000000000..255487744d --- /dev/null +++ b/lib/std/special/ssp.zig @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2015-2020 Zig Contributors +// This file is part of [zig](https://ziglang.org/), which is MIT licensed. +// The MIT license requires this copyright notice to be included in all copies +// and substantial portions of the software. +// +// Small Zig reimplementation of gcc's libssp. +// +// This library implements most of the builtins required by the stack smashing +// protection as implemented by gcc&clang. +const std = @import("std"); +const builtin = std.builtin; + +// Missing exports: +// - __gets_chk +// - __mempcpy_chk +// - __snprintf_chk +// - __sprintf_chk +// - __stpcpy_chk +// - __vsnprintf_chk +// - __vsprintf_chk + +extern fn strncpy(dest: [*:0]u8, src: [*:0]const u8, n: usize) callconv(.C) [*:0]u8; +extern fn memset(dest: ?[*]u8, c: u8, n: usize) callconv(.C) ?[*]u8; +extern fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize) callconv(.C) ?[*]u8; +extern fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) callconv(.C) ?[*]u8; + +// Avoid dragging in the runtime safety mechanisms into this .o file. +pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { + @setCold(true); + if (@hasDecl(std.os, "abort")) + std.os.abort(); + while (true) {} +} + +export fn __stack_chk_fail() callconv(.C) noreturn { + @panic("stack smashing detected"); +} + +export fn __chk_fail() callconv(.C) noreturn { + @panic("buffer overflow detected"); +} + +// Emitted when targeting some architectures (eg. i386) +// XXX: This symbol should be hidden +export fn __stack_chk_fail_local() callconv(.C) noreturn { + __stack_chk_fail(); +} + +// XXX: Initialize the canary with random data +export var __stack_chk_guard: usize = blk: { + var buf = [1]u8{0} ** @sizeOf(usize); + buf[@sizeOf(usize) - 1] = 255; + buf[@sizeOf(usize) - 2] = '\n'; + break :blk @bitCast(usize, buf); +}; + +export fn __strcpy_chk(dest: [*:0]u8, src: [*:0]const u8, dest_n: usize) callconv(.C) [*:0]u8 { + @setRuntimeSafety(false); + + var i: usize = 0; + while (i < dest_n and src[i] != 0) : (i += 1) { + dest[i] = src[i]; + } + + if (i == dest_n) __chk_fail(); + + dest[i] = 0; + + return dest; +} + +export fn __strncpy_chk(dest: [*:0]u8, src: [*:0]const u8, n: usize, dest_n: usize) callconv(.C) [*:0]u8 { + if (dest_n < n) __chk_fail(); + return strncpy(dest, src, n); +} + +export fn __strcat_chk(dest: [*:0]u8, src: [*:0]const u8, dest_n: usize) callconv(.C) [*:0]u8 { + @setRuntimeSafety(false); + + var avail = dest_n; + + var dest_end: usize = 0; + while (avail > 0 and dest[dest_end] != 0) : (dest_end += 1) { + avail -= 1; + } + + if (avail < 1) __chk_fail(); + + var i: usize = 0; + while (avail > 0 and src[i] != 0) : (i += 1) { + dest[dest_end + i] = src[i]; + avail -= 1; + } + + if (avail < 1) __chk_fail(); + + dest[dest_end + i] = 0; + + return dest; +} + +export fn __strncat_chk(dest: [*:0]u8, src: [*:0]const u8, n: usize, dest_n: usize) callconv(.C) [*:0]u8 { + @setRuntimeSafety(false); + + var avail = dest_n; + + var dest_end: usize = 0; + while (avail > 0 and dest[dest_end] != 0) : (dest_end += 1) { + avail -= 1; + } + + if (avail < 1) __chk_fail(); + + var i: usize = 0; + while (avail > 0 and i < n and src[i] != 0) : (i += 1) { + dest[dest_end + i] = src[i]; + avail -= 1; + } + + if (avail < 1) __chk_fail(); + + dest[dest_end + i] = 0; + + return dest; +} + +export fn __memcpy_chk(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize, dest_n: usize) callconv(.C) ?[*]u8 { + if (dest_n < n) __chk_fail(); + return memcpy(dest, src, n); +} + +export fn __memmove_chk(dest: ?[*]u8, src: ?[*]const u8, n: usize, dest_n: usize) callconv(.C) ?[*]u8 { + if (dest_n < n) __chk_fail(); + return memmove(dest, src, n); +} + +export fn __memset_chk(dest: ?[*]u8, c: u8, n: usize, dest_n: usize) callconv(.C) ?[*]u8 { + if (dest_n < n) __chk_fail(); + return memset(dest, c, n); +} |
