Skip to content

Commit

Permalink
all: do not allow uninitialized function pointers
Browse files Browse the repository at this point in the history
  • Loading branch information
medvednikov committed Aug 3, 2023
1 parent 4531373 commit 428fd7f
Show file tree
Hide file tree
Showing 28 changed files with 110 additions and 64 deletions.
6 changes: 3 additions & 3 deletions vlib/cli/command.v
Expand Up @@ -18,9 +18,9 @@ pub mut:
description string
man_description string
version string
pre_execute FnCommandCallback
execute FnCommandCallback
post_execute FnCommandCallback
pre_execute FnCommandCallback = unsafe { nil }
execute FnCommandCallback = unsafe { nil }
post_execute FnCommandCallback = unsafe { nil }
disable_help bool
disable_man bool
disable_version bool
Expand Down
15 changes: 12 additions & 3 deletions vlib/datatypes/bloom_filter.v
Expand Up @@ -4,13 +4,22 @@ module datatypes

[heap]
struct BloomFilter[T] {
hash_func fn (T) u32 // hash function, input [T] , output u32
table_size int // every entry is one-bit, packed into `table`
num_functions int // 1~16
// TODO V bug
hash_func fn (T) u32 = unsafe { nil } // hash function, input [T] , output u32
// hash_func fn (T) u32 = empty_cb // hash function, input [T] , output u32
table_size int // every entry is one-bit, packed into `table`
num_functions int // 1~16
mut:
table []u8
}

/*
TODO maybe allow pointing to generic fns?
fn empty_cb[T](x T) u32 {
panic('empty BloomFilter.hash_func callback')
}
*/

const (
// Salt values(random values). These salts are XORed with the output of the hash function to give multiple unique hashes.
salts = [
Expand Down
4 changes: 2 additions & 2 deletions vlib/eventbus/eventbus.v
Expand Up @@ -19,8 +19,8 @@ mut:

struct EventHandler[T] {
name T
handler EventHandlerFn
receiver voidptr = unsafe { nil }
handler EventHandlerFn = unsafe { nil }
receiver voidptr = unsafe { nil }
once bool
}

Expand Down
10 changes: 5 additions & 5 deletions vlib/fontstash/fontstash_structs.c.v
Expand Up @@ -6,15 +6,15 @@ pub struct C.FONSparams {
flags char
userPtr voidptr
// int (*renderCreate)(void* uptr, int width, int height)
renderCreate fn (uptr voidptr, width int, height int) int
renderCreate fn (uptr voidptr, width int, height int) int = unsafe { nil }
// int (*renderResize)(void* uptr, int width, int height)
renderResize fn (uptr voidptr, width int, height int) int
renderResize fn (uptr voidptr, width int, height int) int = unsafe { nil }
// void (*renderUpdate)(void* uptr, int* rect, const unsigned char* data)
renderUpdate fn (uptr voidptr, rect &int, data &u8)
renderUpdate fn (uptr voidptr, rect &int, data &u8) = unsafe { nil }
// void (*renderDraw)(void* uptr, const float* verts, const float* tcoords, const unsigned int* colors, int nverts)
renderDraw fn (uptr voidptr, verts &f32, tcoords &f32, colors &u32, nverts int)
renderDraw fn (uptr voidptr, verts &f32, tcoords &f32, colors &u32, nverts int) = unsafe { nil }
// void (*renderDelete)(void* uptr)
renderDelete fn (uptr voidptr)
renderDelete fn (uptr voidptr) = unsafe { nil }
}

pub struct C.FONSquad {
Expand Down
6 changes: 3 additions & 3 deletions vlib/regex/regex.v
Expand Up @@ -248,7 +248,7 @@ mut:
// counters for quantifier check (repetitions)
rep int
// validator function pointer
validator FnValidator
validator FnValidator = unsafe { nil }
// groups variables
group_neg bool // negation flag for the group, 0 => no negation > 0 => negataion
group_rep int // repetition of the group
Expand Down Expand Up @@ -376,7 +376,7 @@ fn (mut re RE) reset_src() {
******************************************************************************/
struct BslsStruct {
ch rune // meta char
validator FnValidator // validator function pointer
validator FnValidator = unsafe { nil } // validator function pointer
}

const (
Expand Down Expand Up @@ -463,7 +463,7 @@ mut:
cc_type int // type of cc token
ch0 rune // first char of the interval a-b a in this case
ch1 rune // second char of the interval a-b b in this case
validator FnValidator // validator function pointer
validator FnValidator = unsafe { nil } // validator function pointer
}

enum CharClass_parse_state {
Expand Down
6 changes: 3 additions & 3 deletions vlib/sokol/gfx/gfx_allocator_and_logger.c.v
Expand Up @@ -5,14 +5,14 @@ import sokol.memory
[typedef]
pub struct C.sg_allocator {
pub mut:
alloc memory.FnAllocatorAlloc
free memory.FnAllocatorFree
alloc memory.FnAllocatorAlloc = unsafe { nil }
free memory.FnAllocatorFree = unsafe { nil }
user_data voidptr
}

[typedef]
pub struct C.sg_logger {
pub mut:
log_cb memory.FnLogCb
log_cb memory.FnLogCb = unsafe { nil }
user_data voidptr
}
8 changes: 4 additions & 4 deletions vlib/sokol/gfx/gfx_structs.c.v
Expand Up @@ -46,17 +46,17 @@ pub type GLContextDesc = C.sg_gl_context_desc

struct C.sg_metal_context_desc {
device voidptr
renderpass_descriptor_cb fn () voidptr
drawable_cb fn () voidptr
renderpass_descriptor_cb fn () voidptr = unsafe { nil }
drawable_cb fn () voidptr = unsafe { nil }
}

pub type MetalContextDesc = C.sg_metal_context_desc

struct C.sg_d3d11_context_desc {
device voidptr
device_context voidptr
render_target_view_cb fn () voidptr
depth_stencil_view_cb fn () voidptr
render_target_view_cb fn () voidptr = unsafe { nil }
depth_stencil_view_cb fn () voidptr = unsafe { nil }
}

pub type D3D11ContextDesc = C.sg_d3d11_context_desc
Expand Down
6 changes: 3 additions & 3 deletions vlib/sokol/sapp/sapp_allocator_and_logger.c.v
Expand Up @@ -5,14 +5,14 @@ import sokol.memory
[typedef]
pub struct C.sapp_allocator {
pub mut:
alloc memory.FnAllocatorAlloc
free memory.FnAllocatorFree
alloc memory.FnAllocatorAlloc = unsafe { nil }
free memory.FnAllocatorFree = unsafe { nil }
user_data voidptr
}

[typedef]
pub struct C.sapp_logger {
pub mut:
log_cb memory.FnLogCb
log_cb memory.FnLogCb = unsafe { nil }
user_data voidptr
}
21 changes: 11 additions & 10 deletions vlib/sokol/sapp/sapp_structs.c.v
Expand Up @@ -37,18 +37,19 @@ pub type IconDesc = C.sapp_icon_desc
[typedef]
pub struct C.sapp_desc {
pub:
init_cb fn () // these are the user-provided callbacks without user data
frame_cb fn ()
cleanup_cb fn ()
event_cb fn (&Event) //&sapp_event)
fail_cb fn (&u8)
// these are the user-provided callbacks without user data
init_cb fn () = unsafe { nil }
frame_cb fn () = unsafe { nil }
cleanup_cb fn () = unsafe { nil }
event_cb fn (&Event) = unsafe { nil } // &sapp_event
fail_cb fn (&u8) = unsafe { nil }

user_data voidptr // these are the user-provided callbacks with user data
init_userdata_cb fn (voidptr)
frame_userdata_cb fn (voidptr)
cleanup_userdata_cb fn (voidptr)
event_userdata_cb fn (&Event, voidptr)
fail_userdata_cb fn (&char, voidptr)
init_userdata_cb fn (voidptr) = unsafe { nil }
frame_userdata_cb fn (voidptr) = unsafe { nil }
cleanup_userdata_cb fn (voidptr) = unsafe { nil }
event_userdata_cb fn (&Event, voidptr) = unsafe { nil }
fail_userdata_cb fn (&char, voidptr) = unsafe { nil }

width int // the preferred width of the window / canvas
height int // the preferred height of the window / canvas
Expand Down
4 changes: 2 additions & 2 deletions vlib/sokol/sfons/sfons_funcs.c.v
Expand Up @@ -6,8 +6,8 @@ import sokol.memory
[typedef]
pub struct C.sfons_allocator_t {
pub:
alloc memory.FnAllocatorAlloc
free memory.FnAllocatorFree
alloc memory.FnAllocatorAlloc = unsafe { nil }
free memory.FnAllocatorFree = unsafe { nil }
user_data voidptr
}

Expand Down
6 changes: 3 additions & 3 deletions vlib/sokol/sgl/sgl_allocator_and_logger.c.v
Expand Up @@ -5,14 +5,14 @@ import sokol.memory
[typedef]
pub struct C.sgl_allocator_t {
pub mut:
alloc memory.FnAllocatorAlloc
free memory.FnAllocatorFree
alloc memory.FnAllocatorAlloc = unsafe { nil }
free memory.FnAllocatorFree = unsafe { nil }
user_data voidptr
}

[typedef]
pub struct C.sgl_logger_t {
pub mut:
log_cb memory.FnLogCb
log_cb memory.FnLogCb = unsafe { nil }
user_data voidptr
}
8 changes: 7 additions & 1 deletion vlib/sync/pool/pool.v
Expand Up @@ -24,9 +24,15 @@ mut:

pub type ThreadCB = fn (mut p PoolProcessor, idx int, task_id int) voidptr

fn empty_cb(mut p PoolProcessor, idx int, task_id int) voidptr {
unsafe {
return nil
}
}

pub struct PoolProcessorConfig {
maxjobs int
callback ThreadCB
callback ThreadCB = empty_cb
}

// new_pool_processor returns a new PoolProcessor instance.
Expand Down
2 changes: 1 addition & 1 deletion vlib/toml/ast/walker/walker.v
Expand Up @@ -15,7 +15,7 @@ pub interface Modifier {
pub type InspectorFn = fn (value &ast.Value, data voidptr) !

struct Inspector {
inspector_callback InspectorFn
inspector_callback InspectorFn = unsafe { nil }
mut:
data voidptr
}
Expand Down
6 changes: 5 additions & 1 deletion vlib/v/ast/walker/walker.v
Expand Up @@ -10,8 +10,12 @@ mut:

pub type InspectorFn = fn (node &ast.Node, data voidptr) bool

fn empty_callback(node &ast.Node, data voidptr) bool {
panic('empty ast.walker')
}

struct Inspector {
inspector_callback InspectorFn
inspector_callback InspectorFn = empty_callback
mut:
data voidptr
}
Expand Down
13 changes: 13 additions & 0 deletions vlib/v/checker/struct.v
Expand Up @@ -70,6 +70,19 @@ fn (mut c Checker) struct_decl(mut node ast.StructDecl) {
}
}
}
// Do not allow uninitialized `fn` fields, or force `?fn`
// (allow them in `C.` structs)
if !c.is_builtin_mod && node.language == .v {
sym := c.table.sym(field.typ)
if sym.kind == .function {
if !field.typ.has_flag(.option) && !field.has_default_expr
&& field.attrs.filter(it.name == 'required').len == 0 {
error_msg := 'uninitialized `fn` struct fields are not allowed, since they can result in segfaults; use `?fn` or initialize the field with `=` (if you absolutely want to have unsafe function pointers, use `= unsafe { nil }`)'
c.note(error_msg, field.pos)
}
}
}

if field.has_default_expr {
c.expected_type = field.typ
field.default_expr_typ = c.expr(mut field.default_expr)
Expand Down
4 changes: 2 additions & 2 deletions vlib/v/checker/tests/duplicate_field_method_err.vv
@@ -1,5 +1,5 @@
struct St{
attr fn()
struct St {
attr fn () = unsafe { nil }
}

fn (s St) attr() {}
Expand Down
@@ -1,7 +1,13 @@
vlib/v/checker/tests/fn_check_for_matching_option_result_in_fields.vv:2:2: notice: uninitialized `fn` struct fields are not allowed, since they can result in segfaults; use `?fn` or initialize the field with `=` (if you absolutely want to have unsafe function pointers, use `= unsafe { nil }`)
1 | struct Abc {
2 | f fn (voidptr)
| ~~~~~~~~~~~~~~
3 | }
4 |
vlib/v/checker/tests/fn_check_for_matching_option_result_in_fields.vv:7:3: error: cannot assign to field `f`: expected `fn (voidptr)`, not `fn (voidptr) ?`
5 | fn main() {
6 | a := Abc{
7 | f: fn (data voidptr) ? {}
| ~~~~~~~~~~~~~~~~~~~~~~~~~
8 | }
9 | println(a)
9 | println(a)
15 changes: 11 additions & 4 deletions vlib/v/checker/tests/generics_struct_init_err.out
@@ -1,3 +1,10 @@
vlib/v/checker/tests/generics_struct_init_err.vv:14:2: notice: uninitialized `fn` struct fields are not allowed, since they can result in segfaults; use `?fn` or initialize the field with `=` (if you absolutely want to have unsafe function pointers, use `= unsafe { nil }`)
12 |
13 | struct FnHolder2[T] {
14 | func fn (int) int
| ~~~~~~~~~~~~~~~~~
15 | }
16 |
vlib/v/checker/tests/generics_struct_init_err.vv:67:8: error: could not infer generic type `T` in generic struct `FnHolder2[T]`
65 | ret = holder_call_22(neg, 5)
66 | assert ret == -5
Expand All @@ -6,14 +13,14 @@ vlib/v/checker/tests/generics_struct_init_err.vv:67:8: error: could not infer ge
68 | assert ret == -6
69 | }
vlib/v/checker/tests/generics_struct_init_err.vv:22:7: error: generic struct init must specify type parameter, e.g. Foo[T]
20 |
20 |
21 | fn holder_call_1[T](func T, a int) int {
22 | h := FnHolder1{func}
| ~~~~~~~~~~~~~~~
23 | return h.call(a)
24 | }
vlib/v/checker/tests/generics_struct_init_err.vv:27:7: error: generic struct init must specify type parameter, e.g. Foo[T]
25 |
25 |
26 | fn holder_call_2[T](func T, a int) int {
27 | h := FnHolder2{func}
| ~~~~~~~~~~~~~~~
Expand All @@ -34,14 +41,14 @@ vlib/v/checker/tests/generics_struct_init_err.vv:39:7: error: generic struct ini
40 | return h.call(a)
41 | }
vlib/v/checker/tests/generics_struct_init_err.vv:44:9: error: generic struct init must specify type parameter, e.g. Foo[T]
42 |
42 |
43 | fn holder_call_12[T](func T, a int) int {
44 | return FnHolder1{func}.call(a)
| ~~~~~~~~~~~~~~~
45 | }
46 |
vlib/v/checker/tests/generics_struct_init_err.vv:48:9: error: generic struct init must specify type parameter, e.g. Foo[T]
46 |
46 |
47 | fn holder_call_22[T](func T, a int) int {
48 | return FnHolder2{func}.call(a)
| ~~~~~~~~~~~~~~~
Expand Down
2 changes: 1 addition & 1 deletion vlib/v/checker/tests/method_call_arg_no_mut_err.vv
Expand Up @@ -9,7 +9,7 @@ struct Alarm {
struct Clock {
mut:
arr []Alarm
alarm_fkt Fkt
alarm_fkt Fkt = unsafe { nil }
}

fn fkt(mut a Alarm) {
Expand Down
2 changes: 1 addition & 1 deletion vlib/v/checker/tests/option_fn_err.vv
Expand Up @@ -13,7 +13,7 @@ const (
)

struct Data {
f fn (int)
f fn (int) = unsafe { nil }
mut:
value int = bar(0)
opt ?int = bar(0)
Expand Down
@@ -1,5 +1,5 @@
struct App {
cb fn(x int) // the function signature doesn't make a difference
cb fn(x int) = unsafe { nil } // the function signature doesn't make a difference
}

fn main() {
Expand Down
4 changes: 2 additions & 2 deletions vlib/v/checker/tests/struct_field_type_err.vv
Expand Up @@ -2,8 +2,8 @@ struct Data {
mut:
n int
b bool
f1 fn (voidptr)
f2 fn (...voidptr)
f1 fn (voidptr) [required]
f2 fn (...voidptr) [required]
data &Data
}

Expand Down
2 changes: 1 addition & 1 deletion vlib/v/checker/tests/unknown_type_in_anon_fn.out
@@ -1,7 +1,7 @@
vlib/v/checker/tests/unknown_type_in_anon_fn.vv:5:10: error: unknown type `Another`
3 | struct Struc{
4 | mut:
5 | f fn (s Another, i int)?
5 | f fn (s Another, i int)? [required]
| ~~~~~~~
6 | }
7 |
2 changes: 1 addition & 1 deletion vlib/v/checker/tests/unknown_type_in_anon_fn.vv
Expand Up @@ -2,7 +2,7 @@ module main

struct Struc{
mut:
f fn (s Another, i int)?
f fn (s Another, i int)? [required]
}

fn main() {}

0 comments on commit 428fd7f

Please sign in to comment.