all: do not allow uninitialized function pointers

This commit is contained in:
Alexander Medvednikov 2023-08-03 21:06:32 +03:00
parent 453137384e
commit 428fd7f57f
28 changed files with 110 additions and 64 deletions

View File

@ -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

View File

@ -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 = [

View File

@ -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
}

View File

@ -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 {

View File

@ -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
@ -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 (
@ -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 {

View File

@ -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
}

View File

@ -46,8 +46,8 @@ 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
@ -55,8 +55,8 @@ 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

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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.

View File

@ -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
}

View File

@ -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
}

View File

@ -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)

View File

@ -1,5 +1,5 @@
struct St{
attr fn()
struct St {
attr fn () = unsafe { nil }
}
fn (s St) attr() {}

View File

@ -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)

View File

@ -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
@ -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}
| ~~~~~~~~~~~~~~~
@ -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)
| ~~~~~~~~~~~~~~~

View File

@ -9,7 +9,7 @@ struct Alarm {
struct Clock {
mut:
arr []Alarm
alarm_fkt Fkt
alarm_fkt Fkt = unsafe { nil }
}
fn fkt(mut a Alarm) {

View File

@ -13,7 +13,7 @@ const (
)
struct Data {
f fn (int)
f fn (int) = unsafe { nil }
mut:
value int = bar(0)
opt ?int = bar(0)

View File

@ -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() {

View File

@ -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
}

View File

@ -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 |

View File

@ -2,7 +2,7 @@ module main
struct Struc{
mut:
f fn (s Another, i int)?
f fn (s Another, i int)? [required]
}
fn main() {}

View File

@ -1,9 +1,9 @@
module main
struct Ok {
alibaba fn (Ok, )
alibaba fn (Ok, ) = unsafe { nil }
}
struct OkInt {
a fn (int, )
a fn (int, ) = unsafe { nil }
}

View File

@ -4,4 +4,4 @@ vlib/v/parser/tests/struct_field_required_fn_option_type.vv:6:7: error: field `F
6 | f := Foo{}
| ~~~~~
7 | println(f)
8 | }
8 | }

View File

@ -1,5 +1,5 @@
vlib/v/parser/tests/struct_field_required_fn_result_type.vv:6:7: error: field `Foo.foo` must be initialized
4 |
4 |
5 | fn main() {
6 | f := Foo{}
| ~~~~~

View File

@ -446,7 +446,7 @@ type ControllerHandler = fn (ctx Context, mut url urllib.URL, host string, tid i
pub struct ControllerPath {
pub:
path string
handler ControllerHandler
handler ControllerHandler = unsafe { nil }
pub mut:
host string
}
@ -1111,7 +1111,7 @@ fn (mut w Worker[T]) process_incomming_requests() {
[params]
pub struct PoolParams[T] {
handler fn () T [required]
handler fn () T [required] = unsafe { nil }
nr_workers int = runtime.nr_jobs()
}