checker: warn if C.m* or C.s* functions are called outside unsafe blocks (#5869)

pull/5901/head
Nick Treleaven 2020-07-20 18:06:41 +01:00 committed by GitHub
parent 1a5236e53d
commit a74cbf55c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 175 additions and 88 deletions

View File

@ -73,7 +73,9 @@ fn new_array_from_c_array(len, cap, elm_size int, c_array voidptr) array {
cap: cap_
}
// TODO Write all memory functions (like memcpy) in V
C.memcpy(arr.data, c_array, len * elm_size)
unsafe {
C.memcpy(arr.data, c_array, len * elm_size)
}
return arr
}
@ -125,7 +127,9 @@ pub fn (a array) repeat(count int) array {
for i in 0..count {
if a.len > 0 && a.element_size == sizeof(array) {
ary := array{}
C.memcpy(&ary, a.data, sizeof(array))
unsafe {
C.memcpy(&ary, a.data, sizeof(array))
}
ary_clone := ary.clone()
unsafe {
C.memcpy(arr.get_unsafe(i * a.len), &ary_clone, a.len * a.element_size)
@ -516,7 +520,9 @@ pub fn copy(dst, src []byte) int {
if dst.len > 0 && src.len > 0 {
mut min := 0
min = if dst.len < src.len { dst.len } else { src.len }
C.memcpy(byteptr(dst.data), src[..min].data, dst.element_size * min)
unsafe {
C.memcpy(byteptr(dst.data), src[..min].data, dst.element_size * min)
}
return min
}
return 0

View File

@ -150,7 +150,7 @@ pub fn malloc(n int) byteptr {
nr_mallocs++
return res
} $else {
ptr := C.malloc(n)
ptr := unsafe {C.malloc(n)}
if ptr == 0 {
panic('malloc($n) failed')
}
@ -177,12 +177,14 @@ TODO
[unsafe_fn]
pub fn v_realloc(b byteptr, n u32) byteptr {
$if prealloc {
new_ptr := malloc(int(n))
size := 0 //malloc_size(b)
C.memcpy(new_ptr, b, size)
return new_ptr
unsafe {
new_ptr := malloc(int(n))
size := 0 //malloc_size(b)
C.memcpy(new_ptr, b, size)
return new_ptr
}
} $else {
ptr := C.realloc(b, n)
ptr := unsafe {C.realloc(b, n)}
if ptr == 0 {
panic('realloc($n) failed')
}
@ -217,8 +219,10 @@ pub fn memdup(src voidptr, sz int) voidptr {
if sz == 0 {
return vcalloc(1)
}
mem := malloc(sz)
return C.memcpy(mem, src, sz)
unsafe {
mem := malloc(sz)
return C.memcpy(mem, src, sz)
}
}
fn v_ptr_free(ptr voidptr) {

View File

@ -62,7 +62,9 @@ const (
fn builtin_init() {
if is_atty(1) > 0 {
C.SetConsoleMode(C.GetStdHandle(C.STD_OUTPUT_HANDLE), C.ENABLE_PROCESSED_OUTPUT | 0x0004) // enable_virtual_terminal_processing
C.setbuf(C.stdout, 0)
unsafe {
C.setbuf(C.stdout, 0)
}
}
add_unhandled_exception_handler()
}

View File

@ -125,7 +125,8 @@ fn C.mktime() int
fn C.gettimeofday() int
fn C.sleep() int
[trusted_fn]
fn C.sleep(int) int
fn C.usleep() int
@ -152,9 +153,11 @@ fn C.tolower() int
fn C.toupper() int
[trusted_fn]
fn C.getchar() int
[trusted_fn]
fn C.strerror(int) charptr

View File

@ -88,7 +88,9 @@ fn fast_string_eq(a, b string) bool {
if a.len != b.len {
return false
}
return C.memcmp(a.str, b.str, b.len) == 0
unsafe {
return C.memcmp(a.str, b.str, b.len) == 0
}
}
// Dynamic array with very low growth factor
@ -502,11 +504,13 @@ pub fn (d DenseArray) clone() DenseArray {
cap: d.cap
len: d.len
deletes: d.deletes
keys: &string(malloc(int(d.cap * sizeof(string))))
values: byteptr(malloc(int(d.cap * u32(d.value_bytes))))
keys: unsafe {&string(malloc(int(d.cap * sizeof(string))))}
values: unsafe {byteptr(malloc(int(d.cap * u32(d.value_bytes))))}
}
unsafe {
C.memcpy(res.keys, d.keys, d.cap * sizeof(string))
C.memcpy(res.values, d.values, d.cap * u32(d.value_bytes))
}
C.memcpy(res.keys, d.keys, d.cap * sizeof(string))
C.memcpy(res.values, d.values, d.cap * u32(d.value_bytes))
return res
}

View File

@ -73,7 +73,9 @@ fn opt_ok(data voidptr, size int) Option {
res := Option{
ok: true
}
C.memcpy(res.data, data, size)
unsafe {
C.memcpy(res.data, data, size)
}
return res
}

View File

@ -79,7 +79,9 @@ fn (mut m SortedMap) set(key string, value voidptr) {
}
parent.split_child(child_index, mut node)
if key == parent.keys[child_index] {
C.memcpy(parent.values[child_index], value, m.value_bytes)
unsafe {
C.memcpy(parent.values[child_index], value, m.value_bytes)
}
return
}
node = if key < parent.keys[child_index] {
@ -91,7 +93,9 @@ fn (mut m SortedMap) set(key string, value voidptr) {
mut i := 0
for i < node.len && key > node.keys[i] { i++ }
if i != node.len && key == node.keys[i] {
C.memcpy(node.values[i], value, m.value_bytes)
unsafe {
C.memcpy(node.values[i], value, m.value_bytes)
}
return
}
if isnil(node.children) {
@ -103,7 +107,9 @@ fn (mut m SortedMap) set(key string, value voidptr) {
}
node.keys[j + 1] = key
node.values[j + 1] = malloc(m.value_bytes)
C.memcpy(node.values[j + 1], value, m.value_bytes)
unsafe {
C.memcpy(node.values[j + 1], value, m.value_bytes)
}
node.len++
m.len++
return
@ -150,7 +156,9 @@ fn (m SortedMap) get(key string, out voidptr) bool {
mut i := node.len - 1
for i >= 0 && key < node.keys[i] { i-- }
if i != -1 && key == node.keys[i] {
C.memcpy(out, node.values[i], m.value_bytes)
unsafe {
C.memcpy(out, node.values[i], m.value_bytes)
}
return true
}
if isnil(node.children) {

View File

@ -66,8 +66,9 @@ pub mut:
len int
}
[unsafe_fn]
pub fn vstrlen(s byteptr) int {
return C.strlen(charptr(s))
return unsafe {C.strlen(charptr(s))}
}
// Converts a C string to a V string.
@ -107,14 +108,14 @@ pub fn tos3(s charptr) string {
}
return string{
str: byteptr(s)
len: C.strlen(s)
len: unsafe {C.strlen(s)}
}
}
pub fn tos_lit(s charptr) string {
return string{
str: byteptr(s)
len: C.strlen(s)
len: unsafe {C.strlen(s)}
is_lit: 1
}
}
@ -371,7 +372,9 @@ fn (s string) eq(a string) bool {
if s.len != a.len {
return false
}
return C.memcmp(s.str, a.str, a.len) == 0
unsafe {
return C.memcmp(s.str, a.str, a.len) == 0
}
}
// !=
@ -1379,7 +1382,9 @@ pub fn (s string) bytes() []byte {
return []
}
mut buf := []byte{ len:s.len }
C.memcpy(buf.data, s.str, s.len)
unsafe {
C.memcpy(buf.data, s.str, s.len)
}
return buf
}

View File

@ -32,11 +32,15 @@ pub fn setenv(name string, value string, overwrite bool) int {
$if windows {
format := '$name=$value'
if overwrite {
return C._putenv(format.str)
unsafe {
return C._putenv(format.str)
}
}
return -1
} $else {
return C.setenv(charptr(name.str), charptr(value.str), overwrite)
unsafe {
return C.setenv(charptr(name.str), charptr(value.str), overwrite)
}
}
}

View File

@ -32,7 +32,9 @@ pub:
// it supports windows for regular files but it doesn't matter if you use owner, group or others when checking permissions on windows
pub fn inode(path string) FileMode {
mut attr := C.stat{}
C.stat(charptr(path.str), &attr)
unsafe {
C.stat(charptr(path.str), &attr)
}
mut typ := FileType.regular
if attr.st_mode & u32(C.S_IFMT) == u32(C.S_IFDIR) {

View File

@ -140,14 +140,16 @@ pub fn (mut f File) flush() {
// file_size returns the size of the file located in `path`.
pub fn file_size(path string) int {
mut s := C.stat{}
$if windows {
$if tinyc {
C.stat(charptr(path.str), voidptr(&s))
unsafe {
$if windows {
$if tinyc {
C.stat(charptr(path.str), &s)
} $else {
C._wstat(path.to_wide(), voidptr(&s))
}
} $else {
C._wstat(path.to_wide(), voidptr(&s))
C.stat(charptr(path.str), &s)
}
} $else {
C.stat(charptr(path.str), voidptr(&s))
}
return s.st_size
}
@ -193,7 +195,9 @@ pub fn cp(old, new string) ? {
}
}
from_attr := C.stat{}
C.stat(charptr(old.str), &from_attr)
unsafe {
C.stat(charptr(old.str), &from_attr)
}
if C.chmod(charptr(new.str), from_attr.st_mode) < 0 {
return error_with_code('failed to set permissions for $new', int(-1))
}
@ -444,9 +448,13 @@ pub fn system(cmd string) int {
$if windows {
// overcome bug in system & _wsystem (cmd) when first char is quote `"`
wcmd := if cmd.len > 1 && cmd[0] == `"` && cmd[1] != `"` { '"$cmd"' } else { cmd }
ret = C._wsystem(wcmd.to_wide())
unsafe {
ret = C._wsystem(wcmd.to_wide())
}
} $else {
ret = C.system(charptr(cmd.str))
unsafe {
ret = C.system(charptr(cmd.str))
}
}
if ret == -1 {
print_c_errno()
@ -1095,7 +1103,7 @@ pub fn is_dir(path string) bool {
return false
} $else {
statbuf := C.stat{}
if C.stat(charptr(path.str), &statbuf) != 0 {
if unsafe {C.stat(charptr(path.str), &statbuf)} != 0 {
return false
}
// ref: https://code.woboq.org/gcc/include/sys/stat.h.html
@ -1231,8 +1239,11 @@ pub fn walk(path string, f fn(path string)) {
return
}
[unsafe_fn]
pub fn signal(signum int, handler voidptr) {
C.signal(signum, handler)
unsafe {
C.signal(signum, handler)
}
}
pub fn fork() int {
@ -1260,7 +1271,9 @@ pub fn wait() int {
pub fn file_last_mod_unix(path string) int {
attr := C.stat{}
// # struct stat attr;
C.stat(charptr(path.str), &attr)
unsafe {
C.stat(charptr(path.str), &attr)
}
// # stat(path.str, &attr);
return attr.st_mtime
// # return attr.st_mtime ;

View File

@ -121,7 +121,7 @@ pub fn mkdir(path string) ?bool {
}
}
*/
r := C.mkdir(charptr(apath.str), 511)
r := unsafe {C.mkdir(charptr(apath.str), 511)}
if r == -1 {
return error(posix_get_error_msg(C.errno))
}

View File

@ -42,8 +42,8 @@ pub fn parse_rfc2822(s string) ?Time {
mm := pos / 3 + 1
mut tmstr := byteptr(0)
unsafe { tmstr = malloc(s.len * 2) }
count := C.snprintf(charptr(tmstr), (s.len * 2), '%s-%02d-%s %s', fields[3].str, mm,
fields[1].str, fields[4].str)
count := unsafe {C.snprintf(charptr(tmstr), (s.len * 2), '%s-%02d-%s %s', fields[3].str, mm,
fields[1].str, fields[4].str)}
return parse(tos(tmstr, count))
}
@ -67,10 +67,10 @@ pub fn parse_iso8601(s string) ?Time {
mut offset_hour := 0
mut offset_min := 0
count := C.sscanf(charptr(s.str), "%4d-%2d-%2d%c%2d:%2d:%2d.%6d%c%2d:%2d", &year, &month, &day,
count := unsafe {C.sscanf(charptr(s.str), "%4d-%2d-%2d%c%2d:%2d:%2d.%6d%c%2d:%2d", &year, &month, &day,
&time_char, &hour, &minute,
&second, &mic_second, &plus_min,
&offset_hour, &offset_min)
&offset_hour, &offset_min)}
if count != 11 {
return error('Invalid 8601 format')

View File

@ -1096,12 +1096,16 @@ pub fn (stmt Stmt) position() token.Position {
// field table.Field.default_expr, which should be ast.Expr
pub fn fe2ex(x table.FExpr) Expr {
res := Expr{}
C.memcpy(&res, &x, sizeof(Expr))
unsafe {
C.memcpy(&res, &x, sizeof(Expr))
}
return res
}
pub fn ex2fe(x Expr) table.FExpr {
res := table.FExpr{}
C.memcpy(&res, &x, sizeof(table.FExpr))
unsafe {
C.memcpy(&res, &x, sizeof(table.FExpr))
}
return res
}

View File

@ -1128,6 +1128,12 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
if f.is_deprecated {
c.warn('function `$f.name` has been deprecated', call_expr.pos)
}
if f.is_unsafe && !c.inside_unsafe &&
f.language == .c && f.name[2] in [`m`, `s`] && f.mod == 'builtin' {
// builtin C.m*, C.s* only - temp
c.warn('function `$f.name` must be called from an `unsafe` block',
call_expr.pos)
}
if f.is_generic && f.return_type.has_flag(.generic) {
rts := c.table.get_type_symbol(f.return_type)
if rts.kind == .struct_ {

View File

@ -1,34 +0,0 @@
vlib/v/checker/tests/pointer_arithmetic_should_be_checked.v:4:6: error: pointer arithmetic is only allowed in `unsafe` blocks
2 | v := 5
3 | mut p := &v
4 | p++
| ~~
5 | p += 2
6 | _ := v
vlib/v/checker/tests/pointer_arithmetic_should_be_checked.v:5:7: error: pointer arithmetic is only allowed in `unsafe` blocks
3 | mut p := &v
4 | p++
5 | p += 2
| ~~
6 | _ := v
7 | }
vlib/v/checker/tests/pointer_arithmetic_should_be_checked.v:11:14: error: pointer arithmetic is only allowed in `unsafe` blocks
9 | fn test_ptr_infix() {
10 | v := 4
11 | mut q := &v - 1
| ^
12 | q = q + 3
13 | _ := q
vlib/v/checker/tests/pointer_arithmetic_should_be_checked.v:12:9: error: pointer arithmetic is only allowed in `unsafe` blocks
10 | v := 4
11 | mut q := &v - 1
12 | q = q + 3
| ^
13 | _ := q
14 | _ := v
vlib/v/checker/tests/pointer_arithmetic_should_be_checked.v:24:7: error: method `S1.f` must be called from an `unsafe` block
22 | fn test_funcs() {
23 | s := S1{}
24 | s.f()
| ~~~
25 | }

View File

@ -0,0 +1,13 @@
vlib/v/checker/tests/unsafe_c_calls_should_be_checked.v:3:16: error: function `C.malloc` must be called from an `unsafe` block
1 |
2 | fn test_c() {
3 | mut p := C.malloc(4)
| ~~~~~~~~~
4 | s := 'hope'
5 | C.memcpy(p, s.str, 4)
vlib/v/checker/tests/unsafe_c_calls_should_be_checked.v:5:7: error: function `C.memcpy` must be called from an `unsafe` block
3 | mut p := C.malloc(4)
4 | s := 'hope'
5 | C.memcpy(p, s.str, 4)
| ~~~~~~~~~~~~~~~~~~~
6 | }

View File

@ -0,0 +1,6 @@
fn test_c() {
mut p := C.malloc(4)
s := 'hope'
C.memcpy(p, s.str, 4)
}

View File

@ -0,0 +1,34 @@
vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.v:4:6: error: pointer arithmetic is only allowed in `unsafe` blocks
2 | v := 5
3 | mut p := &v
4 | p++
| ~~
5 | p += 2
6 | _ := v
vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.v:5:7: error: pointer arithmetic is only allowed in `unsafe` blocks
3 | mut p := &v
4 | p++
5 | p += 2
| ~~
6 | _ := v
7 | }
vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.v:11:14: error: pointer arithmetic is only allowed in `unsafe` blocks
9 | fn test_ptr_infix() {
10 | v := 4
11 | mut q := &v - 1
| ^
12 | q = q + 3
13 | _ := q
vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.v:12:9: error: pointer arithmetic is only allowed in `unsafe` blocks
10 | v := 4
11 | mut q := &v - 1
12 | q = q + 3
| ^
13 | _ := q
14 | _ := v
vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.v:24:7: error: method `S1.f` must be called from an `unsafe` block
22 | fn test_funcs() {
23 | s := S1{}
24 | s.f()
| ~~~
25 | }

View File

@ -620,7 +620,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
if word.len != 2 {
verror('opcodes format: xx xx xx xx')
}
b := C.strtol(charptr(word.str), 0, 16)
b := unsafe {C.strtol(charptr(word.str), 0, 16)}
// b := word.byte()
// println('"$word" $b')
g.write8(b)

View File

@ -132,7 +132,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
p.top_level_statement_start()
start_pos := p.tok.position()
is_deprecated := 'deprecated' in p.attrs
is_unsafe := 'unsafe_fn' in p.attrs
mut is_unsafe := 'unsafe_fn' in p.attrs
is_pub := p.tok.kind == .key_pub
if is_pub {
p.next()
@ -141,6 +141,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
p.open_scope()
// C. || JS.
language := if p.tok.kind == .name && p.tok.lit == 'C' {
is_unsafe = !('trusted_fn' in p.attrs)
table.Language.c
} else if p.tok.kind == .name && p.tok.lit == 'JS' {
table.Language.js

View File

@ -27,7 +27,9 @@ pub const (
pub fn vhash() string {
mut buf := [50]byte
buf[0] = 0
C.snprintf(charptr(buf), 50, '%s', C.V_COMMIT_HASH)
unsafe {
C.snprintf(charptr(buf), 50, '%s', C.V_COMMIT_HASH)
}
return tos_clone(buf)
}
@ -97,7 +99,9 @@ pub fn githash(should_get_from_filesystem bool) string {
}
mut buf := [50]byte
buf[0] = 0
C.snprintf(charptr(buf), 50, '%s', C.V_CURRENT_COMMIT_HASH)
unsafe {
C.snprintf(charptr(buf), 50, '%s', C.V_CURRENT_COMMIT_HASH)
}
return tos_clone(buf)
}