1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

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

This commit is contained in:
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_ cap: cap_
} }
// TODO Write all memory functions (like memcpy) in V // 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 return arr
} }
@ -125,7 +127,9 @@ pub fn (a array) repeat(count int) array {
for i in 0..count { for i in 0..count {
if a.len > 0 && a.element_size == sizeof(array) { if a.len > 0 && a.element_size == sizeof(array) {
ary := array{} ary := array{}
C.memcpy(&ary, a.data, sizeof(array)) unsafe {
C.memcpy(&ary, a.data, sizeof(array))
}
ary_clone := ary.clone() ary_clone := ary.clone()
unsafe { unsafe {
C.memcpy(arr.get_unsafe(i * a.len), &ary_clone, a.len * a.element_size) 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 { if dst.len > 0 && src.len > 0 {
mut min := 0 mut min := 0
min = if dst.len < src.len { dst.len } else { src.len } 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 min
} }
return 0 return 0

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -32,11 +32,15 @@ pub fn setenv(name string, value string, overwrite bool) int {
$if windows { $if windows {
format := '$name=$value' format := '$name=$value'
if overwrite { if overwrite {
return C._putenv(format.str) unsafe {
return C._putenv(format.str)
}
} }
return -1 return -1
} $else { } $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 // 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 { pub fn inode(path string) FileMode {
mut attr := C.stat{} mut attr := C.stat{}
C.stat(charptr(path.str), &attr) unsafe {
C.stat(charptr(path.str), &attr)
}
mut typ := FileType.regular mut typ := FileType.regular
if attr.st_mode & u32(C.S_IFMT) == u32(C.S_IFDIR) { 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`. // file_size returns the size of the file located in `path`.
pub fn file_size(path string) int { pub fn file_size(path string) int {
mut s := C.stat{} mut s := C.stat{}
$if windows { unsafe {
$if tinyc { $if windows {
C.stat(charptr(path.str), voidptr(&s)) $if tinyc {
C.stat(charptr(path.str), &s)
} $else {
C._wstat(path.to_wide(), voidptr(&s))
}
} $else { } $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 return s.st_size
} }
@ -193,7 +195,9 @@ pub fn cp(old, new string) ? {
} }
} }
from_attr := C.stat{} 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 { if C.chmod(charptr(new.str), from_attr.st_mode) < 0 {
return error_with_code('failed to set permissions for $new', int(-1)) return error_with_code('failed to set permissions for $new', int(-1))
} }
@ -444,9 +448,13 @@ pub fn system(cmd string) int {
$if windows { $if windows {
// overcome bug in system & _wsystem (cmd) when first char is quote `"` // overcome bug in system & _wsystem (cmd) when first char is quote `"`
wcmd := if cmd.len > 1 && cmd[0] == `"` && cmd[1] != `"` { '"$cmd"' } else { cmd } 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 { } $else {
ret = C.system(charptr(cmd.str)) unsafe {
ret = C.system(charptr(cmd.str))
}
} }
if ret == -1 { if ret == -1 {
print_c_errno() print_c_errno()
@ -1095,7 +1103,7 @@ pub fn is_dir(path string) bool {
return false return false
} $else { } $else {
statbuf := C.stat{} statbuf := C.stat{}
if C.stat(charptr(path.str), &statbuf) != 0 { if unsafe {C.stat(charptr(path.str), &statbuf)} != 0 {
return false return false
} }
// ref: https://code.woboq.org/gcc/include/sys/stat.h.html // 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 return
} }
[unsafe_fn]
pub fn signal(signum int, handler voidptr) { pub fn signal(signum int, handler voidptr) {
C.signal(signum, handler) unsafe {
C.signal(signum, handler)
}
} }
pub fn fork() int { pub fn fork() int {
@ -1260,7 +1271,9 @@ pub fn wait() int {
pub fn file_last_mod_unix(path string) int { pub fn file_last_mod_unix(path string) int {
attr := C.stat{} attr := C.stat{}
// # struct stat attr; // # struct stat attr;
C.stat(charptr(path.str), &attr) unsafe {
C.stat(charptr(path.str), &attr)
}
// # stat(path.str, &attr); // # stat(path.str, &attr);
return attr.st_mtime return attr.st_mtime
// # 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 { if r == -1 {
return error(posix_get_error_msg(C.errno)) 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 mm := pos / 3 + 1
mut tmstr := byteptr(0) mut tmstr := byteptr(0)
unsafe { tmstr = malloc(s.len * 2) } unsafe { tmstr = malloc(s.len * 2) }
count := C.snprintf(charptr(tmstr), (s.len * 2), '%s-%02d-%s %s', fields[3].str, mm, count := unsafe {C.snprintf(charptr(tmstr), (s.len * 2), '%s-%02d-%s %s', fields[3].str, mm,
fields[1].str, fields[4].str) fields[1].str, fields[4].str)}
return parse(tos(tmstr, count)) return parse(tos(tmstr, count))
} }
@ -67,10 +67,10 @@ pub fn parse_iso8601(s string) ?Time {
mut offset_hour := 0 mut offset_hour := 0
mut offset_min := 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, &time_char, &hour, &minute,
&second, &mic_second, &plus_min, &second, &mic_second, &plus_min,
&offset_hour, &offset_min) &offset_hour, &offset_min)}
if count != 11 { if count != 11 {
return error('Invalid 8601 format') 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 // field table.Field.default_expr, which should be ast.Expr
pub fn fe2ex(x table.FExpr) Expr { pub fn fe2ex(x table.FExpr) Expr {
res := Expr{} res := Expr{}
C.memcpy(&res, &x, sizeof(Expr)) unsafe {
C.memcpy(&res, &x, sizeof(Expr))
}
return res return res
} }
pub fn ex2fe(x Expr) table.FExpr { pub fn ex2fe(x Expr) table.FExpr {
res := table.FExpr{} res := table.FExpr{}
C.memcpy(&res, &x, sizeof(table.FExpr)) unsafe {
C.memcpy(&res, &x, sizeof(table.FExpr))
}
return res 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 { if f.is_deprecated {
c.warn('function `$f.name` has been deprecated', call_expr.pos) 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) { if f.is_generic && f.return_type.has_flag(.generic) {
rts := c.table.get_type_symbol(f.return_type) rts := c.table.get_type_symbol(f.return_type)
if rts.kind == .struct_ { 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 { if word.len != 2 {
verror('opcodes format: xx xx xx xx') 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() // b := word.byte()
// println('"$word" $b') // println('"$word" $b')
g.write8(b) g.write8(b)

View File

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

View File

@ -27,7 +27,9 @@ pub const (
pub fn vhash() string { pub fn vhash() string {
mut buf := [50]byte mut buf := [50]byte
buf[0] = 0 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) return tos_clone(buf)
} }
@ -97,7 +99,9 @@ pub fn githash(should_get_from_filesystem bool) string {
} }
mut buf := [50]byte mut buf := [50]byte
buf[0] = 0 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) return tos_clone(buf)
} }