v icon indicating copy to clipboard operation
v copied to clipboard

[WIP] cgen: check for null when calling function pointers in structs

Open trufae opened this issue 4 years ago • 13 comments

See test case, there are some tests that are not passing because the check is not done properly for some scopes.

Also I think it would be interesting to avoid having those checks for unsafe {} blocks.

See the test as an example

Current fail:

/tmp/v/v2.15775295321930989009.tmp.c: In function ‘array map_keys(map*)’:
26
/tmp/v/v2.15775295321930989009.tmp.c:14836:62: error: second operand to the conditional operator is of type ‘void’, but the third operand is neither a throw-expression nor of type ‘void’
27
14836 |     /* null check */ (m->clone_fn)? m->clone_fn(item, pkey): 0;
28
      |                                                              ^

I'm happy if anyone with more knowledge on the compiler internals and spare time can take my PR and finish it+merge. otherwise i will need to clarify the following things:

  • How to check if the call is inside an unsafe{} block?
  • Do we want to get a warning at runtime (or assert) instead of silently not calling the function if the pointer is null?
  • Should this catch or {} blocks for this too?
  • May -prod builds not check for this?

trufae avatar May 03 '21 00:05 trufae

We were considering optional function fields or [required] fields to solve this issue:

https://github.com/vlang/v/discussions/7610#discussioncomment-257982

Hiding null functions like this can lead to hard to debug bugs.

medvednikov avatar May 03 '21 01:05 medvednikov

What would this do for functions that return values?

medvednikov avatar May 03 '21 01:05 medvednikov

i have updated the PR description to respond your questions. i have some more questions too :D but i think it's worth discussing/clarifying to address this

trufae avatar May 03 '21 10:05 trufae

It may be more general to generate dummy functions, that have the same signature, but that just return the default value for the return type, and initialize the fn callback fields with those, instead of 0. This will not need checks and skips at each call site, and will not segfault.

spytheman avatar May 03 '21 17:05 spytheman

How to check if the call is inside an unsafe{} block?

Currently the cgen stage can not check that efficiently, unlike the checker, but it is easy to add. You need to add a new field in struct Gen, around vlib/v/gen/c/cgen.v:77 , say is_unsafe bool, then modify vlib/v/gen/c/cgen.v:3231 to set g.is_unsafe to true, and g.is_unsafe = false afterwards, same with vlib/v/gen/c/cgen.v:1085 .

spytheman avatar May 03 '21 17:05 spytheman

just updated the pr, didnt had much spare time. the unsafe check is not clear to me because the rebasing changed the line numbers. But still, forcing true/false on some scopes doesnt seems like honoring the user defined unsafe blocks :?

trufae avatar May 29 '21 10:05 trufae

./v -cc g++-9 -o v2 cmd/v && ./v2 -cc g++-9 -o v3 cmd/v
==================
/tmp/v/v2.11056817583672041563.tmp.c: In function ‘void map_set(map*, voidptr, voidptr)’:
/tmp/v/v2.11056817583672041563.tmp.c:15043:59: error: second operand to the conditional operator is of type ‘void’, but the third operand is neither a throw-expression nor of type ‘void’
15043 |   /* null check */ (m->clone_fn)? m->clone_fn(pkey, key): 0;

medvednikov avatar Jun 13 '21 13:06 medvednikov

I'm all for @spytheman's solution, generating dummy function and assigning them by default, rather than having this check everytime the function is called

Gladear avatar Jun 14 '21 08:06 Gladear

The problem i see in this approach is that silently calling a null pointer function and getting a value in exchange doesn't seems correct to me. I see some more issues to cover this properly.

  • All function pointers should be treated as [unsafe]
  • Calling an unsafe function should be only happening from inside unsafe { blocks
  • The [unsafe] tag is ignored in a variety of situations and there's no easy way to

Additionaly i think that after making all this we need to clarify:

  • As long as the function call is unsafe, the user should be in charge to check for the null
  • Return optional so the user can handle those errors programatically (maybe this invalidates the unsafe block requirement)
  • Blindly return 0/null/empty-string/.. which seems wrong to me, despite being the most practical solution.

trufae avatar Jun 14 '21 08:06 trufae

The issue aiming t solve this pr is in the roadmap. Would be good to know what would be the path to take here ^^

trufae avatar Oct 07 '21 00:10 trufae

ping?

JalonSolov avatar Jul 06 '22 12:07 JalonSolov

@trufae Hi, is this still actual? Should somebody from the community rebase this PR and try to merge it (in case you don't have time/are not interested in it anymore)

ArtemkaKun avatar Aug 24 '22 08:08 ArtemkaKun

@trufae Can we get some example code, to see how the current version V does against what you were trying to solve? Since you started this, V has gotten the Option fields discussed, etc.

JalonSolov avatar Dec 17 '22 01:12 JalonSolov

Running the test file with the latest version of V gives the following output:

---- Testing... --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 FAIL   120.412 ms /home/jalon/struct_fields_storing_functions_test.v
[136, 1, 2, 2, 153]
RUNTIME ERROR: invalid memory access
hello world

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Failed command 1:    '/home/jalon/git/v/v'   -o '/tmp/v_1000/tsession_7f82478aa740_184466969129049/struct_fields_storing_functions_test' '/home/jalon/struct_fields_storing_functions_test.v'
Summary for all V _test.v files: 1 failed, 1 total. Runtime: 120 ms, on 1 job.

So... no more segfaults, at least. I haven't delved far enough in to see why it gets the invalid memory access, but that could simply be the null pointer.

Unless someone comes up with a good reason otherwise, I plan to close this PR soon, as "no longer needed".

@spytheman @medvednikov @trufae

JalonSolov avatar Jul 12 '23 15:07 JalonSolov

No comments, I think this can be closed

ArtemkaKun avatar Jul 23 '23 14:07 ArtemkaKun