sv-benchmarks
sv-benchmarks copied to clipboard
Unreach-call verdict of ldv-linux-4.2-rc1/linux-4.2-rc1.tar.xz-08_1a-drivers--media--dvb-core--dvb-core.ko-entry_point.cil.out.yml
In our own testing Goblint gave the result true for ldv-linux-4.2-rc1/linux-4.2-rc1.tar.xz-08_1a-drivers--media--dvb-core--dvb-core.ko-entry_point.cil.out.yml but its expected verdict is false. Obviously we want to fix our unsoundness so I've been desperately trying to understand this program and how it can reach the error. Unfortunately I haven't been successful so I'm asking about it here to avoid going truly insane: is there really a violation or could the expected verdict be itself wrong?
Below are a few things which have made me suspect the latter.
Previous SV-COMPs
I looked through the results of previous SV-COMPs which included this benchmark and I couldn't find any tool to have solved this correctly, so no violation witness to inspect either. There were only two cases ever where a tool gave a non-timeout non-error result:
- SymDIVINE in SV-COMP 2017 with true, no (useful) witness;
- SeaHorn-F16 in SV-COMP 2016 with true, no (useful) witness.
Goblint's result
Goblint's true output also comes with a correctness witness witness.txt (renamed to .txt for GitHub). Unfortunately I haven't been able to validate it. I tried both CPAchecker and Ultimate (without time limit and 20GB of memory) and both eventually ran out of that memory.
What Goblint's own output says more directly is that supposedly large parts of this program are dead code.
Manual analysis
Finally I turned myself into a verifier in order to try to understand the program and its supposed violation. Working backwards from error locations I managed to convince myself of the same thing: all the ways I could see for reaching the error are dead code due to how some state variables are initialized and manipulated.
For reference, below is a rough dump of my internal state that lead me to that conclusion.
My manual backwards analysis notes
-
reach_errorcalled by:ldv_errorcalled by:ldv_module_putcalled by:ldv_module_put_11called by:dvb_device_openifdvbdevfromdvb_minorsis not NULL (dead, see below)
ldv_module_put_81called by:dvb_ca_en50221_io_open(dead, see below)
ldv_module_put_82called by:dvb_ca_en50221_io_release(dead, see below)
ldv_module_put_118called by:dvb_net_do_ioctl(dead, see below)
ldv_module_put_119called by:dvb_net_do_ioctl(dead, see below)
ldv_module_put_121called by:dvb_net_do_ioctl(dead, see below)
ldv_module_put_and_exit(dead)
ldv_check_final_stateifldv_module_refcounteris not 1 (dead, see below), called by:main(end)
-
dvb_minorswritten by:- zero initialized
dvb_register_devicecalled by:dvb_dmxdev_init(dead)dvb_ca_en50221_init(dead)dvb_register_frontend(dead)dvb_net_init(dead)
dvb_unregister_deviceto zero (dead, irrelevant)
-
ldv_module_refcounterwritten by:- initialized to 1
ldv_module_get(dead)ldv_try_module_getcalled by:ldv_try_module_get_10called by:dvb_device_openifdvbdevfromdvb_minorsis not NULL (dead, see above)
ldv_try_module_get_80called by:dvb_ca_en50221_io_open(dead, see below)
ldv_try_module_get_117called by:dvb_net_do_ioctl(dead, see below)
ldv_try_module_get_120called by:dvb_net_do_ioctl(dead, see below)
ldv_module_put(dead, see above)
-
dvb_ca_en50221_io_opencalled by:mainifldv_state_variable_8is 1 (dead)- pointer in
dvb_ca_fops- pointer in
dvbdev_ca- used in
dvb_ca_en50221_init(dead)
- used in
- pointer in
-
dvb_ca_en50221_io_releasecalled by:ldv_main_exported_8ifldv_state_variable_8is 2 (dead), called by:mainifldv_state_variable_8is not 0 (dead)
- pointer in
dvb_ca_fops- pointer in
dvbdev_ca- used in
dvb_ca_en50221_init(dead)
- used in
- pointer in
-
ldv_state_variable_8written by:main(begin), initialized to 0ldv_main_exported_8called by:mainifldv_state_variable_8is not 0 (dead)
-
dvb_net_do_ioctlcalled by:dvb_net_ioctlviadvb_usercopy, called by:ldv_main_exported_4ifldv_state_variable_4is 2 (dead), called by:mainifldv_state_variable_4is not 0 (dead)
-
ldv_state_variable_4written by:main(begin), initialized to 0ldv_main_exported_4called by:mainifldv_state_variable_4is not 0 (dead)
@mutilin?
@sim642
Simmo, the intention of the benchmark was to demonstrate a violation of Module get/put rule.
In this example there is a mismatch between try_module_get expanded from fops_get and module_put from fops_replace in dvb_device_open.
As you correctly wrote, current example misses some initialization of dvb_minors, which should be done by dvb_register_device from dvb_dmxdev_init. Alternatively, it may be initialized by arbitrary values.
@mutilin Since you know the structure of these benchmarks better than I do, could you open a PR to put the missing initialization to some appropriate place which would restore the original intent of the benchmark?
I assume that would be preferable to changing the expected verdict to true, which would match the existing benchmark code but misses the original intent.
I think it is better to move it to some TODO directory or remove, because now I do not have time to fix it.