1//!
2//! Small Zig reimplementation of gcc's libssp.
3//!
4//! This library implements most of the builtins required by the stack smashing
5//! protection as implemented by gcc&clang.
6//! Missing exports:
7//! - __gets_chk
8//! - __mempcpy_chk
9//! - __snprintf_chk
10//! - __sprintf_chk
11//! - __stpcpy_chk
12//! - __vsnprintf_chk
13//! - __vsprintf_chk
14
15const std = @import("std");
16
17extern fn strncpy(dest: [*:0]u8, src: [*:0]const u8, n: usize) callconv(.C) [*:0]u8;
18extern fn memset(dest: ?[*]u8, c: u8, n: usize) callconv(.C) ?[*]u8;
19extern fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize) callconv(.C) ?[*]u8;
20extern fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) callconv(.C) ?[*]u8;
21
22// Avoid dragging in the runtime safety mechanisms into this .o file.
23pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) noreturn {
24    _ = msg;
25    _ = error_return_trace;
26    @setCold(true);
27    std.os.abort();
28}
29
30export fn __stack_chk_fail() callconv(.C) noreturn {
31    @panic("stack smashing detected");
32}
33
34export fn __chk_fail() callconv(.C) noreturn {
35    @panic("buffer overflow detected");
36}
37
38// Emitted when targeting some architectures (eg. i386)
39// XXX: This symbol should be hidden
40export fn __stack_chk_fail_local() callconv(.C) noreturn {
41    __stack_chk_fail();
42}
43
44// XXX: Initialize the canary with random data
45export var __stack_chk_guard: usize = blk: {
46    var buf = [1]u8{0} ** @sizeOf(usize);
47    buf[@sizeOf(usize) - 1] = 255;
48    buf[@sizeOf(usize) - 2] = '\n';
49    break :blk @bitCast(usize, buf);
50};
51
52export fn __strcpy_chk(dest: [*:0]u8, src: [*:0]const u8, dest_n: usize) callconv(.C) [*:0]u8 {
53    @setRuntimeSafety(false);
54
55    var i: usize = 0;
56    while (i < dest_n and src[i] != 0) : (i += 1) {
57        dest[i] = src[i];
58    }
59
60    if (i == dest_n) __chk_fail();
61
62    dest[i] = 0;
63
64    return dest;
65}
66
67export fn __strncpy_chk(dest: [*:0]u8, src: [*:0]const u8, n: usize, dest_n: usize) callconv(.C) [*:0]u8 {
68    if (dest_n < n) __chk_fail();
69    return strncpy(dest, src, n);
70}
71
72export fn __strcat_chk(dest: [*:0]u8, src: [*:0]const u8, dest_n: usize) callconv(.C) [*:0]u8 {
73    @setRuntimeSafety(false);
74
75    var avail = dest_n;
76
77    var dest_end: usize = 0;
78    while (avail > 0 and dest[dest_end] != 0) : (dest_end += 1) {
79        avail -= 1;
80    }
81
82    if (avail < 1) __chk_fail();
83
84    var i: usize = 0;
85    while (avail > 0 and src[i] != 0) : (i += 1) {
86        dest[dest_end + i] = src[i];
87        avail -= 1;
88    }
89
90    if (avail < 1) __chk_fail();
91
92    dest[dest_end + i] = 0;
93
94    return dest;
95}
96
97export fn __strncat_chk(dest: [*:0]u8, src: [*:0]const u8, n: usize, dest_n: usize) callconv(.C) [*:0]u8 {
98    @setRuntimeSafety(false);
99
100    var avail = dest_n;
101
102    var dest_end: usize = 0;
103    while (avail > 0 and dest[dest_end] != 0) : (dest_end += 1) {
104        avail -= 1;
105    }
106
107    if (avail < 1) __chk_fail();
108
109    var i: usize = 0;
110    while (avail > 0 and i < n and src[i] != 0) : (i += 1) {
111        dest[dest_end + i] = src[i];
112        avail -= 1;
113    }
114
115    if (avail < 1) __chk_fail();
116
117    dest[dest_end + i] = 0;
118
119    return dest;
120}
121
122export fn __memcpy_chk(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize, dest_n: usize) callconv(.C) ?[*]u8 {
123    if (dest_n < n) __chk_fail();
124    return memcpy(dest, src, n);
125}
126
127export fn __memmove_chk(dest: ?[*]u8, src: ?[*]const u8, n: usize, dest_n: usize) callconv(.C) ?[*]u8 {
128    if (dest_n < n) __chk_fail();
129    return memmove(dest, src, n);
130}
131
132export fn __memset_chk(dest: ?[*]u8, c: u8, n: usize, dest_n: usize) callconv(.C) ?[*]u8 {
133    if (dest_n < n) __chk_fail();
134    return memset(dest, c, n);
135}
136