diff --git a/vlib/builtin/array.v b/vlib/builtin/array.v index 2925cb1fbb..3154dbda53 100644 --- a/vlib/builtin/array.v +++ b/vlib/builtin/array.v @@ -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 diff --git a/vlib/builtin/builtin.v b/vlib/builtin/builtin.v index 81d24ac612..ece4bdc3c1 100644 --- a/vlib/builtin/builtin.v +++ b/vlib/builtin/builtin.v @@ -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) { diff --git a/vlib/builtin/builtin_windows.c.v b/vlib/builtin/builtin_windows.c.v index 9e48332134..0059603d9e 100644 --- a/vlib/builtin/builtin_windows.c.v +++ b/vlib/builtin/builtin_windows.c.v @@ -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() } diff --git a/vlib/builtin/cfns.c.v b/vlib/builtin/cfns.c.v index 496c8dd0fa..da27376b52 100644 --- a/vlib/builtin/cfns.c.v +++ b/vlib/builtin/cfns.c.v @@ -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 diff --git a/vlib/builtin/map.v b/vlib/builtin/map.v index 410c8dd861..c80b003d8f 100644 --- a/vlib/builtin/map.v +++ b/vlib/builtin/map.v @@ -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 } diff --git a/vlib/builtin/option.v b/vlib/builtin/option.v index 7f698487c6..3c33c850d3 100644 --- a/vlib/builtin/option.v +++ b/vlib/builtin/option.v @@ -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 } diff --git a/vlib/builtin/sorted_map.v b/vlib/builtin/sorted_map.v index 0a00b9388f..3a99fe5f11 100644 --- a/vlib/builtin/sorted_map.v +++ b/vlib/builtin/sorted_map.v @@ -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) { diff --git a/vlib/builtin/string.v b/vlib/builtin/string.v index b87f04f11e..2b5d987248 100644 --- a/vlib/builtin/string.v +++ b/vlib/builtin/string.v @@ -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 } diff --git a/vlib/os/environment.v b/vlib/os/environment.v index ea93c657f4..f2ec075921 100644 --- a/vlib/os/environment.v +++ b/vlib/os/environment.v @@ -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) + } } } diff --git a/vlib/os/inode.v b/vlib/os/inode.v index 7d407a488a..4145ff8a2b 100644 --- a/vlib/os/inode.v +++ b/vlib/os/inode.v @@ -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) { diff --git a/vlib/os/os.v b/vlib/os/os.v index 83ccc3e237..d9f0e442e6 100644 --- a/vlib/os/os.v +++ b/vlib/os/os.v @@ -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 ; diff --git a/vlib/os/os_nix.c.v b/vlib/os/os_nix.c.v index 795887b844..39a8aa4455 100644 --- a/vlib/os/os_nix.c.v +++ b/vlib/os/os_nix.c.v @@ -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)) } diff --git a/vlib/time/parse.v b/vlib/time/parse.v index 7127df88b1..1b48f3cca0 100644 --- a/vlib/time/parse.v +++ b/vlib/time/parse.v @@ -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') diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 77a1207611..3866ecd901 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -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 } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 781d9a84b3..eb9ec0197f 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -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_ { diff --git a/vlib/v/checker/tests/pointer_arithmetic_should_be_checked.out b/vlib/v/checker/tests/pointer_arithmetic_should_be_checked.out deleted file mode 100644 index ab7d9c8fe0..0000000000 --- a/vlib/v/checker/tests/pointer_arithmetic_should_be_checked.out +++ /dev/null @@ -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 | } diff --git a/vlib/v/checker/tests/unsafe_c_calls_should_be_checked.out b/vlib/v/checker/tests/unsafe_c_calls_should_be_checked.out new file mode 100644 index 0000000000..6a2e7aa013 --- /dev/null +++ b/vlib/v/checker/tests/unsafe_c_calls_should_be_checked.out @@ -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 | } diff --git a/vlib/v/checker/tests/unsafe_c_calls_should_be_checked.vv b/vlib/v/checker/tests/unsafe_c_calls_should_be_checked.vv new file mode 100644 index 0000000000..092fe2c1b0 --- /dev/null +++ b/vlib/v/checker/tests/unsafe_c_calls_should_be_checked.vv @@ -0,0 +1,6 @@ + +fn test_c() { + mut p := C.malloc(4) + s := 'hope' + C.memcpy(p, s.str, 4) +} diff --git a/vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.out b/vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.out new file mode 100644 index 0000000000..05aef5b1fb --- /dev/null +++ b/vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.out @@ -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 | } diff --git a/vlib/v/checker/tests/pointer_arithmetic_should_be_checked.vv b/vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.vv similarity index 100% rename from vlib/v/checker/tests/pointer_arithmetic_should_be_checked.vv rename to vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.vv diff --git a/vlib/v/gen/x64/gen.v b/vlib/v/gen/x64/gen.v index 0e28211ba0..b428e7402a 100644 --- a/vlib/v/gen/x64/gen.v +++ b/vlib/v/gen/x64/gen.v @@ -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) diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 6523afebfa..0c63dab250 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -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 diff --git a/vlib/v/util/util.v b/vlib/v/util/util.v index 8f6dc947e2..e7ead5268f 100644 --- a/vlib/v/util/util.v +++ b/vlib/v/util/util.v @@ -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) }