vlib: enable more satnitized memleak detection runs without false positives on the CI (#23200)

This commit is contained in:
Felipe Pena 2024-12-20 11:09:16 -03:00 committed by GitHub
parent 903e349a7c
commit b0772193f8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 89 additions and 22 deletions

View File

@ -0,0 +1,13 @@
# websocket tests
leak:mbedtls_ssl_setup
# sqlite tests
leak:*sqlite*
# v
leak:v__*
leak:os__*
leak:string__plus
leak:malloc_uncollectable
leak:new_array_from_c_array
leak:_vinit

View File

@ -75,7 +75,7 @@ concurrency:
jobs:
tests-sanitize-undefined-clang:
runs-on: ubuntu-20.04
timeout-minutes: 240
timeout-minutes: 120
env:
VFLAGS: -cc clang
VJOBS: 1
@ -101,7 +101,7 @@ jobs:
tests-sanitize-undefined-gcc:
runs-on: ubuntu-20.04
timeout-minutes: 240
timeout-minutes: 120
env:
VFLAGS: -cc gcc
VJOBS: 1
@ -126,7 +126,7 @@ jobs:
tests-sanitize-address-clang:
runs-on: ubuntu-20.04
timeout-minutes: 240
timeout-minutes: 300
env:
VFLAGS: -cc clang
VJOBS: 1
@ -146,16 +146,16 @@ jobs:
- name: Recompile V with -cstrict
run: ./v -cg -cstrict -o v cmd/v
- name: Self tests (-fsanitize=address)
run: ASAN_OPTIONS=detect_leaks=0 ./v -cflags "-fsanitize=address,pointer-compare,pointer-subtract" test-self vlib
run: ASAN_OPTIONS=detect_leaks=1:fast_unwind_on_malloc=0 LSAN_OPTIONS=max_leaks=1:print_suppressions=0:suppressions=.github/workflows/run_sanitizers_leak.suppressions ./v -cflags "-fsanitize=address,pointer-compare,pointer-subtract" test-self vlib
- name: Self tests (V compiled with -fsanitize=address)
run: ./v -cflags -fsanitize=address -o v cmd/v &&
ASAN_OPTIONS=detect_leaks=0 ./v -cc tcc test-self -asan-compiler vlib
ASAN_OPTIONS=detect_leaks=1:fast_unwind_on_malloc=0 LSAN_OPTIONS=max_leaks=1:print_suppressions=0:suppressions=.github/workflows/run_sanitizers_leak.suppressions ./v -cc tcc test-self -asan-compiler vlib
- name: Build examples (V compiled with -fsanitize=address)
run: ASAN_OPTIONS=detect_leaks=0 ./v build-examples
run: ASAN_OPTIONS=detect_leaks=1:fast_unwind_on_malloc=0 LSAN_OPTIONS=max_leaks=1:print_suppressions=0:suppressions=.github/workflows/run_sanitizers_leak.suppressions ./v build-examples
tests-sanitize-address-msvc:
runs-on: windows-2019
timeout-minutes: 240
timeout-minutes: 30
env:
VFLAGS: -cc msvc
VJOBS: 1
@ -179,7 +179,7 @@ jobs:
tests-sanitize-address-gcc:
runs-on: ubuntu-20.04
timeout-minutes: 240
timeout-minutes: 300
env:
VFLAGS: -cc gcc
VJOBS: 1
@ -199,13 +199,13 @@ jobs:
- name: Recompile V with -cstrict
run: ./v -cg -cstrict -o v cmd/v
- name: Self tests (-fsanitize=address)
run: ASAN_OPTIONS=detect_leaks=0 ./v -cflags -fsanitize=address test-self vlib
run: ASAN_OPTIONS=detect_leaks=1:fast_unwind_on_malloc=0 LSAN_OPTIONS=max_leaks=1:print_suppressions=0:suppressions=.github/workflows/run_sanitizers_leak.suppressions ./v -cflags -fsanitize=address test-self vlib
- name: Self tests (V compiled with -fsanitize=address)
run:
./v -cflags -fsanitize=address,pointer-compare,pointer-subtract -o v cmd/v &&
ASAN_OPTIONS=detect_leaks=0 ./v -cc tcc test-self -asan-compiler vlib
ASAN_OPTIONS=detect_leaks=1:fast_unwind_on_malloc=0 LSAN_OPTIONS=max_leaks=1:print_suppressions=0:suppressions=.github/workflows/run_sanitizers_leak.suppressions ./v -cc tcc test-self -asan-compiler vlib
- name: Build examples (V compiled with -fsanitize=address)
run: ASAN_OPTIONS=detect_leaks=0 ./v build-examples
run: ASAN_OPTIONS=detect_leaks=1:fast_unwind_on_malloc=0 LSAN_OPTIONS=max_leaks=1:print_suppressions=0:suppressions=.github/workflows/run_sanitizers_leak.suppressions ./v build-examples
tests-sanitize-memory-clang:
runs-on: ubuntu-20.04

View File

@ -106,6 +106,9 @@ fn print_backtrace_skipping_top_frames_linux(skipframes int) bool {
output = output.replace(' (discriminator', ': (d.')
eprintln('${output:-55s} | ${addr:14s} | ${beforeaddr}')
}
if sframes.len > 0 {
unsafe { C.free(csymbols) }
}
}
}
}

View File

@ -24,7 +24,11 @@ pub fn compress(data []u8, flags int) ![]u8 {
if u64(out_len) > max_size {
return error('compressed data is too large (${out_len} > ${max_size})')
}
return unsafe { address.vbytes(int(out_len)) }
unsafe {
ret := address.vbytes(int(out_len)).clone()
C.free(address)
return ret
}
}
// decompresses an array of bytes based on providing flags and returns the decompressed bytes in a new array
@ -40,5 +44,10 @@ pub fn decompress(data []u8, flags int) ![]u8 {
if u64(out_len) > max_size {
return error('decompressed data is too large (${out_len} > ${max_size})')
}
return unsafe { address.vbytes(int(out_len)) }
unsafe {
ret := address.vbytes(int(out_len)).clone()
C.free(address)
return ret
}
}

View File

@ -227,3 +227,8 @@ pub fn (pub_key PublicKey) equal(other PublicKey) bool {
res := C.EC_POINT_cmp(group, point1, point2, ctx)
return res == 0
}
// Clear allocated memory for key
pub fn key_free(ec_key &C.EC_KEY) {
C.EC_KEY_free(ec_key)
}

View File

@ -12,6 +12,7 @@ fn test_ecdsa() {
is_valid := pub_key.verify(message, signature) or { panic(err) }
println('Signature valid: ${is_valid}')
assert is_valid
key_free(priv_key.key)
}
fn test_generate_key() ! {
@ -19,6 +20,7 @@ fn test_generate_key() ! {
pub_key, priv_key := generate_key() or { panic(err) }
assert pub_key.key != unsafe { nil }
assert priv_key.key != unsafe { nil }
key_free(priv_key.key)
}
fn test_new_key_from_seed() ! {
@ -27,6 +29,7 @@ fn test_new_key_from_seed() ! {
priv_key := new_key_from_seed(seed) or { panic(err) }
retrieved_seed := priv_key.seed() or { panic(err) }
assert seed == retrieved_seed
key_free(priv_key.key)
}
fn test_sign_and_verify() ! {
@ -36,6 +39,7 @@ fn test_sign_and_verify() ! {
signature := priv_key.sign(message) or { panic(err) }
is_valid := pub_key.verify(message, signature) or { panic(err) }
assert is_valid
key_free(priv_key.key)
}
fn test_seed() ! {
@ -43,6 +47,7 @@ fn test_seed() ! {
_, priv_key := generate_key() or { panic(err) }
seed := priv_key.seed() or { panic(err) }
assert seed.len > 0
key_free(priv_key.key)
}
fn test_public_key() ! {
@ -51,6 +56,9 @@ fn test_public_key() ! {
pub_key1 := priv_key.public_key() or { panic(err) }
pub_key2, _ := generate_key() or { panic(err) }
assert !pub_key1.equal(pub_key2)
key_free(priv_key.key)
key_free(pub_key1.key)
key_free(pub_key2.key)
}
fn test_private_key_equal() ! {
@ -59,6 +67,8 @@ fn test_private_key_equal() ! {
seed := priv_key1.seed() or { panic(err) }
priv_key2 := new_key_from_seed(seed) or { panic(err) }
assert priv_key1.equal(priv_key2)
key_free(priv_key1.key)
key_free(priv_key2.key)
}
fn test_public_key_equal() ! {
@ -67,6 +77,9 @@ fn test_public_key_equal() ! {
pub_key1 := priv_key.public_key() or { panic(err) }
pub_key2 := priv_key.public_key() or { panic(err) }
assert pub_key1.equal(pub_key2)
key_free(priv_key.key)
key_free(pub_key1.key)
key_free(pub_key2.key)
}
fn test_sign_with_new_key_from_seed() ! {
@ -78,6 +91,8 @@ fn test_sign_with_new_key_from_seed() ! {
pub_key := priv_key.public_key() or { panic(err) }
is_valid := pub_key.verify(message, signature) or { panic(err) }
assert is_valid
key_free(priv_key.key)
key_free(pub_key.key)
}
fn test_invalid_signature() ! {
@ -88,9 +103,11 @@ fn test_invalid_signature() ! {
result := pub_key.verify(message, invalid_signature) or {
// Expecting verification to fail
assert err.msg() == 'Failed to verify signature'
key_free(pub_key.key)
return
}
assert !result
key_free(pub_key.key)
}
fn test_different_keys_not_equal() ! {
@ -98,4 +115,6 @@ fn test_different_keys_not_equal() ! {
_, priv_key1 := generate_key() or { panic(err) }
_, priv_key2 := generate_key() or { panic(err) }
assert !priv_key1.equal(priv_key2)
key_free(priv_key1.key)
key_free(priv_key2.key)
}

View File

@ -20,7 +20,7 @@ struct Baby {
}
fn test_main() {
db := sqlite.connect(':memory:')!
mut db := sqlite.connect(':memory:')!
sql db {
create table Parent
@ -80,4 +80,6 @@ fn test_main() {
assert parent[0].children[0].id == 1
assert parent[0].children[1].id == 2
assert parent[0].children.len == 2
db.close()!
}

View File

@ -22,7 +22,7 @@ fn records_by_field[T](db sqlite.DB, fieldname string, value string) ![]T {
}
fn test_main() {
db := sqlite.connect(':memory:')!
mut db := sqlite.connect(':memory:')!
sql db {
create table Blog
}!
@ -40,4 +40,5 @@ fn test_main() {
return
}
assert rows.len == 1
db.close()!
}

View File

@ -11,7 +11,7 @@ pub:
type Content = []u8 | string
struct Host {
pub:
pub mut:
db Connection
}
@ -92,7 +92,8 @@ fn test_can_access_sqlite_result_consts() {
}
fn test_alias_db() {
create_host(sqlite.connect(':memory:')!)!
mut host := create_host(sqlite.connect(':memory:')!)!
host.db.close()!
assert true
}
@ -110,7 +111,9 @@ fn test_exec_param_many() {
'Sam',
]) or {
assert err.code() == 19 // constraint failure
db.close()!
return
}
db.close()!
assert false
}

View File

@ -5,6 +5,7 @@ fn test_object_with_null() {
root.add_item_to_object('name', cjson.create_string('Andre'))
root.add_item_to_object('age', cjson.create_null())
assert root.print_unformatted() == '{"name":"Andre","age":null}'
unsafe { cjson.delete(root) }
}
fn test_creating_complex_json() {
@ -18,4 +19,5 @@ fn test_creating_complex_json() {
println(result)
assert result == '["user",{"username":"foo","password":"bar"}]'
unsafe { cjson.delete(root) }
}

View File

@ -82,6 +82,8 @@ fn C.cJSON_Print(object &C.cJSON) &char
fn C.cJSON_PrintUnformatted(object &C.cJSON) &char
fn C.cJSON_free(voidptr)
//
// version returns the version of cJSON as a string
@ -168,7 +170,9 @@ pub fn (mut obj Node) print() string {
// print serialises the node to a string, without formatting its structure, so the resulting string is shorter/cheaper to transmit.
pub fn (mut obj Node) print_unformatted() string {
mut s := C.cJSON_PrintUnformatted(obj)
return unsafe { tos3(s) }
ret := unsafe { tos_clone(&u8(s)) }
C.cJSON_free(s)
return ret
}
// str returns the unformatted serialisation to string of the given Node.

View File

@ -3,6 +3,7 @@ import net
import net.websocket
import time
@[heap]
struct WebsocketTestResults {
pub mut:
nr_messages int

View File

@ -10,7 +10,7 @@ const const_users_offset = 1
const const_users_offset2 = 1
fn test_orm() {
db := sqlite.connect(':memory:') or { panic(err) }
mut db := sqlite.connect(':memory:') or { panic(err) }
upper_1 := User{
name: 'Test'
@ -31,4 +31,5 @@ fn test_orm() {
} or { panic(err) }
assert result[0].name == 'Test'
db.close()!
}

View File

@ -11,7 +11,7 @@ struct SubStruct {
}
fn test_orm_sub_structs() {
db := sqlite.connect(':memory:') or { panic(err) }
mut db := sqlite.connect(':memory:') or { panic(err) }
sql db {
create table Upper
}!
@ -34,4 +34,5 @@ fn test_orm_sub_structs() {
}!
assert uppers.first().sub.name == upper_1.sub.name
db.close()!
}

View File

@ -6,12 +6,14 @@ struct ORMTableSpecificName {
}
fn test_orm_table_name() {
db := sqlite.connect(':memory:') or { panic(err) }
mut db := sqlite.connect(':memory:') or { panic(err) }
r := sql db {
select from ORMTableSpecificName
} or {
db.close()!
assert true
return
}
db.close()!
assert false
}

View File

@ -10,7 +10,7 @@ fn x(m Movie) int {
}
fn test_sql_statement_inside_fn_call() {
db := sqlite.connect(':memory:') or { panic('failed') }
mut db := sqlite.connect(':memory:') or { panic('failed') }
sql db {
create table Movie
}!
@ -21,4 +21,5 @@ fn test_sql_statement_inside_fn_call() {
dump(x(sql db {
select from Movie where id == 1
}!.first()))
db.close()!
}