master
  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");
 16const common = @import("./common.zig");
 17const builtin = @import("builtin");
 18
 19extern fn memset(dest: ?[*]u8, c: u8, n: usize) callconv(.c) ?[*]u8;
 20extern fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize) callconv(.c) ?[*]u8;
 21extern fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) callconv(.c) ?[*]u8;
 22
 23comptime {
 24    @export(&__stack_chk_fail, .{ .name = "__stack_chk_fail", .linkage = common.linkage, .visibility = common.visibility });
 25    @export(&__chk_fail, .{ .name = "__chk_fail", .linkage = common.linkage, .visibility = common.visibility });
 26    @export(&__stack_chk_guard, .{ .name = "__stack_chk_guard", .linkage = common.linkage, .visibility = common.visibility });
 27    @export(&__strcpy_chk, .{ .name = "__strcpy_chk", .linkage = common.linkage, .visibility = common.visibility });
 28    @export(&__strncpy_chk, .{ .name = "__strncpy_chk", .linkage = common.linkage, .visibility = common.visibility });
 29    @export(&__strcat_chk, .{ .name = "__strcat_chk", .linkage = common.linkage, .visibility = common.visibility });
 30    @export(&__strncat_chk, .{ .name = "__strncat_chk", .linkage = common.linkage, .visibility = common.visibility });
 31    @export(&__memcpy_chk, .{ .name = "__memcpy_chk", .linkage = common.linkage, .visibility = common.visibility });
 32    @export(&__memmove_chk, .{ .name = "__memmove_chk", .linkage = common.linkage, .visibility = common.visibility });
 33    @export(&__memset_chk, .{ .name = "__memset_chk", .linkage = common.linkage, .visibility = common.visibility });
 34}
 35
 36fn __stack_chk_fail() callconv(.c) noreturn {
 37    @panic("stack smashing detected");
 38}
 39
 40fn __chk_fail() callconv(.c) noreturn {
 41    @panic("buffer overflow detected");
 42}
 43
 44// TODO: Initialize the canary with random data
 45var __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 @as(usize, @bitCast(buf));
 50};
 51
 52fn __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
 67fn __strncpy_chk(dest: [*:0]u8, src: [*:0]const u8, n: usize, dest_n: usize) callconv(.c) [*:0]u8 {
 68    @setRuntimeSafety(false);
 69    if (dest_n < n) __chk_fail();
 70    var i: usize = 0;
 71    while (i < n and src[i] != 0) : (i += 1) {
 72        dest[i] = src[i];
 73    }
 74    while (i < n) : (i += 1) {
 75        dest[i] = 0;
 76    }
 77    return dest;
 78}
 79
 80fn __strcat_chk(dest: [*:0]u8, src: [*:0]const u8, dest_n: usize) callconv(.c) [*:0]u8 {
 81    @setRuntimeSafety(false);
 82
 83    var avail = dest_n;
 84
 85    var dest_end: usize = 0;
 86    while (avail > 0 and dest[dest_end] != 0) : (dest_end += 1) {
 87        avail -= 1;
 88    }
 89
 90    if (avail < 1) __chk_fail();
 91
 92    var i: usize = 0;
 93    while (avail > 0 and src[i] != 0) : (i += 1) {
 94        dest[dest_end + i] = src[i];
 95        avail -= 1;
 96    }
 97
 98    if (avail < 1) __chk_fail();
 99
100    dest[dest_end + i] = 0;
101
102    return dest;
103}
104
105fn __strncat_chk(dest: [*:0]u8, src: [*:0]const u8, n: usize, dest_n: usize) callconv(.c) [*:0]u8 {
106    @setRuntimeSafety(false);
107
108    var avail = dest_n;
109
110    var dest_end: usize = 0;
111    while (avail > 0 and dest[dest_end] != 0) : (dest_end += 1) {
112        avail -= 1;
113    }
114
115    if (avail < 1) __chk_fail();
116
117    var i: usize = 0;
118    while (avail > 0 and i < n and src[i] != 0) : (i += 1) {
119        dest[dest_end + i] = src[i];
120        avail -= 1;
121    }
122
123    if (avail < 1) __chk_fail();
124
125    dest[dest_end + i] = 0;
126
127    return dest;
128}
129
130fn __memcpy_chk(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize, dest_n: usize) callconv(.c) ?[*]u8 {
131    if (dest_n < n) __chk_fail();
132    return memcpy(dest, src, n);
133}
134
135fn __memmove_chk(dest: ?[*]u8, src: ?[*]const u8, n: usize, dest_n: usize) callconv(.c) ?[*]u8 {
136    if (dest_n < n) __chk_fail();
137    return memmove(dest, src, n);
138}
139
140fn __memset_chk(dest: ?[*]u8, c: u8, n: usize, dest_n: usize) callconv(.c) ?[*]u8 {
141    if (dest_n < n) __chk_fail();
142    return memset(dest, c, n);
143}