Re: apparent bug about check_free_strict

Toomas Soome <[email protected]> Fri, 21 Nov 2025 20:01:22 +0200
Newsgroups org.kernel.vger.smatch
Message-ID <[email protected]>
Oops, Mail did create it using HTML, resending in plain text.



> On 18. Nov 2024, at 23:17, Toomas Soome <[email protected]> wrote:
>=20
>=20
>=20
>> On 18. Nov 2024, at 17:27, Dan Carpenter <[email protected]> =
wrote:
>>=20
>> On Mon, Nov 18, 2024 at 03:28:57PM +0200, Toomas Soome wrote:
>>>=20
>>>=20
>>>> On 18. Nov 2024, at 14:52, Dan Carpenter <[email protected]> =
wrote:
>>>>=20
>>>> On Mon, Nov 18, 2024 at 01:55:30PM +0200, Toomas Soome wrote:
>>>>> hi!
>>>>>=20
>>>>> I did enable illumos kernel memory allocation/free checks =
(kmem_alloc/kmem_free) and apparently I did find something interesting.
>>>>>=20
>>>>> The warning is:
>>>>> =
/code/illumos-gate/usr/src/tools/proto/root_i386-nd/opt/onbld/bin/i386/sma=
tch: ../../common/os/devcfg.c:8583 e_ddi_retire_device() warn: passing =
freed memory 'pdip'
>>>>> =
/code/illumos-gate/usr/src/tools/proto/root_i386-nd/opt/onbld/bin/i386/sma=
tch: ../../common/os/devcfg.c:8612 e_ddi_retire_device() warn: passing =
freed memory 'dip'
>>>>> =
/code/illumos-gate/usr/src/tools/proto/root_i386-nd/opt/onbld/bin/i386/sma=
tch: ../../common/os/devcfg.c:8621 e_ddi_retire_device() warn: passing =
freed memory =E2=80=98dip'
>>>>>=20
>>>>> The code for first error about pdip is:
>>>>>=20
>>>>> 8572          pdip =3D ddi_get_parent(dip);
>>>>> 8573          ndi_hold_devi(pdip);
>>>>> 8574     8575          /*
>>>>> 8576           * Run devfs_clean() in case dip has no constraints =
and is
>>>>> 8577           * not in use, so is retireable but there are =
dv_nodes holding
>>>>> 8578           * ref-count on the dip. Note that devfs_clean() =
always returns
>>>>> 8579           * success.
>>>>> 8580           */
>>>>> 8581          devnm =3D kmem_alloc(MAXNAMELEN + 1, KM_SLEEP);
>>>>> 8582          (void) ddi_deviname(dip, devnm);
>>>>> 8583          (void) devfs_clean(pdip, devnm + 1, DV_CLEAN_FORCE);
>>>>> 8584          kmem_free(devnm, MAXNAMELEN + 1);
>>>>> 8585     8586          ndi_devi_enter(pdip);
>>>>>=20
>>>>> We get this error about pdip with devfs_clean(), but apparently =
the =E2=80=98freed
>>>>> state is set with ndi_hold_devi(pdip) call; of course the call =
itself is not
>>>>> the quilty one, but the construct is =E2=80=94 as soon as I either =
comment the
>>>>> ndi_hold_devi() out *or* if I move it down before devfs_clean(), =
then
>>>>> the error disappears.
>>>>>=20
>>>>> Therefore, it appears that code segment such as:
>>>>>=20
>>>>> var =3D f();
>>>>> g(var);
>>>>>=20
>>>>> is causing state of var to be set =E2=80=98freed=E2=80=99 and =
check_free_strict.c is ending up
>>>>> spitting out the warning about passing freed memory with next =
function call.
>>>>>=20
>>>>=20
>>>> I don't see anything in ndi_hold_devi() which would mark "pdip" as =
freed.
>>>>=20
>>>=20
>>>=20
>>> As I wrote, I do not think it is really about the function itself, =
it is about
>>> the sequence =E2=80=94 when I moved the ndi_hold_devi() down just =
before the =E2=80=98pdip=E2=80=99 was actually called, then this warning =
did disappear.
>>>=20
>>>> I don't know how to run Smatch on this file...  Could you re-run =
Smatch with the
>>>> --debug=3D"free" option and save the output to a file?  Maybe send =
that output
>>>> along with the whole file or run it against the lates git so I can =
match the
>>>> line numbers up.
>>>>=20
>>>=20
>>> I guess it is a bit more complicated because you will also need the =
illumos specific update to check_free_strict.
>>>=20
>>> The command in usr/src/uts/intel/genunix is run as:
>>>=20
>>> =
/code/illumos-gate/usr/src/tools/proto/root_i386-nd/opt/onbld/bin/i386/sma=
tch --debug=3Dfree -fident -finline -fno-inline-functions -fno-builtin =
-fno-asm -fdiagnostics-show-option -nodefaultlibs -D__sun -m64 =
-mtune=3Dopteron -Ui386 -U__i386 -fno-strict-aliasing =
-fno-unit-at-a-time -fno-optimize-sibling-calls -O2 -D_ASM_INLINES =
-ffreestanding -mno-red-zone -mno-mmx -mno-sse -msave-args -Wall -Wextra =
-g -gdwarf-2 -std=3Dgnu99 -msave-args -Werror -Wno-missing-braces =
-Wno-sign-compare -Wno-unknown-pragmas -Wno-unused-parameter =
-Wno-missing-field-initializers -Winline -Wno-unused -Wno-empty-body =
-p=3Dillumos_kernel --disable=3Duninitialized,check_check_deref -Wno-vla =
-Wno-one-bit-signed-bitfield -Wno-external-function-has-definition =
-Wno-old-style-definition -Wno-strict-prototypes --fatal-checks =
--timeout=3D0 --disable=3Dindex_overflow =
--disable=3Dsigned,all_func_returns -Wno-unused-variable =
-Wno-unused-value -Wno-unused-function -Wno-parentheses =
-Wno-maybe-uninitialized -Wno-clobbered -Wno-empty-body =
-fno-inline-small-functions -fno-inline-functions-called-once =
-fno-ipa-cp -fno-ipa-icf -fno-clone-functions -fno-reorder-functions =
-fno-reorder-blocks-and-partition -fno-aggressive-loop-optimizations =
--param=3Dmax-inline-insns-single=3D450 -fno-shrink-wrap =
-mindirect-branch=3Dthunk-extern -mindirect-branch-register =
-fno-asynchronous-unwind-tables -fstack-protector-strong =
-fno-eliminate-unused-debug-symbols -fno-eliminate-unused-debug-types =
-D_KERNEL -ffreestanding -D_SYSCALL32 -D_SYSCALL32_IMPL -D_ELF64 =
-D_DDI_STRICT -Dsun -D__sun -D__SVR4 -DOPTERON_ERRATUM_88 =
-DOPTERON_ERRATUM_91 -DOPTERON_ERRATUM_93 -DOPTERON_ERRATUM_95 =
-DOPTERON_ERRATUM_99 -DOPTERON_ERRATUM_100 -DOPTERON_ERRATUM_101 =
-DOPTERON_ERRATUM_108 -DOPTERON_ERRATUM_109 -DOPTERON_ERRATUM_121 =
-DOPTERON_ERRATUM_122 -DOPTERON_ERRATUM_123 -DOPTERON_ERRATUM_131 =
-DOPTERON_WORKAROUND_6336786 -DOPTERON_ERRATUM_147 -DOPTERON_ERRATUM_172 =
-DOPTERON_ERRATUM_298 -DOPTERON_ERRATUM_721 -I../../intel -nostdinc =
-I../../common -I/code/illumos-gate/usr/src/common =
-I/code/illumos-gate/usr/src/uts/common/fs/zfs -I../../i86pc -c -o =
/tmp/cw.GCaq5P/cwICaO5P.o ../../common/os/devcfg.c -mcmodel=3Dkernel
>>>=20
>>>=20
>>> I did put the samples of files into =
http://132-104-190-90.sta.estpak.ee/smatch/,
>>> I still need to clean up a bit my smatch repo, that will take a bit =
more time.
>>>=20
>>=20
>> There's something weird going on.  I don't see any problem with the =
changes you
>> have made to check_free_strict.c.  I started to just merge your =
changes but then
>> I realized I don't know who to give authorship credit etc but I'd =
already pushed
>> whatever changes I had on my end so I just made the situation worse.
>>=20
>=20
> :D the kmem_alloc/kmem_free related bits are done by me. But we can =
sort later what we could and should upstream and what maybe not.
>=20
>> You are testing with the latest Smatch right?
>=20
>=20
> Yes. My current head of master branch is:
>=20
> commit 9a427ca57dc8a8b47d021f5f772ac164842bd996 (upstream/master, =
master)
> Author: Dan Carpenter <[email protected]>
> Date:   Thu Nov 14 23:12:46 2024 +0300
>=20
>     db: fix a NULL dereference in get_param()
>=20
>>=20
>> Anyway, I've attached the diff to the latest code below with some =
debugging.
>> Apply the diff to the lastest Smatch and apply the code-diff to the =
devcfg.c
>> and then re-run Smatch.
>>=20
>=20
> Now this is fun. With devcfg.c changes in place, the warnings =
disappeared. So I removed 'if (local_debug)=E2=80=99 from =
check_free_strict.c and, I think we have something to point to:
>=20
> =
/code/illumos-gate/usr/src/tools/proto/root_i386-nd/opt/onbld/bin/i386/sma=
tch: ../../common/os/devcfg.c:8573 e_ddi_retire_device() =
set_param_freed: expr=3D'ndi_hold_devi(pdip)' param=3D0 key=3D'$'
> =
/code/illumos-gate/usr/src/tools/proto/root_i386-nd/opt/onbld/bin/i386/sma=
tch: ../../common/os/devcfg.c:8573 e_ddi_retire_device() set_state new =
[check_free_strict] 'pdip' freed
> ../../common/os/devcfg.c:8573 e_ddi_retire_device() merge =
[check_free_strict] 'pdip' freed(L 8573) + undefined(L 8573) =3D> merged =
(freed, undefined, merged)
> =
/code/illumos-gate/usr/src/tools/proto/root_i386-nd/opt/onbld/bin/i386/sma=
tch: ../../common/os/devcfg.c:8573 e_ddi_retire_device() __set_sm new =
[check_free_strict] pdip fffff7ffe9523b90 =3D 'merged' [merged] (freed, =
undefined, merged)
> =
/code/illumos-gate/usr/src/tools/proto/root_i386-nd/opt/onbld/bin/i386/sma=
tch: ../../common/os/devcfg.c:8583 e_ddi_retire_device() warn: passing =
freed memory 'pdip'
>=20
> and the same for =E2=80=98dip=E2=80=99:
> =
/code/illumos-gate/usr/src/tools/proto/root_i386-nd/opt/onbld/bin/i386/sma=
tch: ../../common/os/devcfg.c:8611 e_ddi_retire_device() =
set_param_freed: expr=3D'is_leaf_node(dip)' param=3D0 key=3D'$'
> =
/code/illumos-gate/usr/src/tools/proto/root_i386-nd/opt/onbld/bin/i386/sma=
tch: ../../common/os/devcfg.c:8611 e_ddi_retire_device() set_state new =
[check_free_strict] 'dip=E2=80=99 freed
>=20
> and we will =
have:/code/illumos-gate/usr/src/tools/proto/root_i386-nd/opt/onbld/bin/i38=
6/smatch: ../../common/os/devcfg.c:8612 e_ddi_retire_device() warn: =
passing freed memory 'dip'
>=20
> So, set_param_freed() does register =E2=80=98pdip=E2=80=99 as freed, =
and this callback is set up as
>=20
> select_return_states_hook(PARAM_FREED, &set_param_freed);
>=20
> thanks,
> toomas


Hi!

I got some time to get back to this issue - yep, it is still present =
with current smatch. So I did use a bit of dtrace - I print out user =
space stack when we do enter set_param_freed(), and to verify the =
expression, I have this update:
 static void set_param_freed(struct expression *expr, int param, char =
*key, char *value)
 {
+       sm_msg("%s: expr=3D'%s' param=3D%d key=3D'%s'", __func__, =
expr_to_str(expr), param, key);
        set_param_helper(expr, param, key, value, &freed);
 }

it allows me to print out the expression with dtrace; the dtrace command =
line is there:

sudo dtrace -n 'pid$target::set_param_freed:entry { self->p =3D 1; =
ustack(); } pid$target::set_param_freed:return {self->p =3D 0;} =
pid$target::expr_to_str:return /self->p/ { printf("%s\n", =
copyinstr(arg1));}' -c =
'/code/illumos-gate/usr/src/tools/proto/root_i386-nd/opt/onbld/bin/i386/sm=
atch -fident -finline -fno-inline-functions -fno-builtin -fno-asm =
-fdiagnostics-show-option -nodefaultlibs -D__sun -m64 -mtune=3Dopteron =
-Ui386 -U__i386 -fno-strict-aliasing -fno-unit-at-a-time =
-fno-optimize-sibling-calls -O2 -D_ASM_INLINES -ffreestanding =
-mno-red-zone -mno-mmx -mno-sse -msave-args -Wall -Wextra -g -gdwarf-4 =
-gstrict-dwarf -std=3Dgnu99 -msave-args -Werror -Wno-missing-braces =
-Wno-sign-compare -Wno-unused-parameter -Wno-missing-field-initializers =
-Winline -Wno-unused -Wno-empty-body -p=3Dillumos_kernel =
--disable=3Duninitialized,check_check_deref -Wno-vla =
-Wno-one-bit-signed-bitfield -Wno-external-function-has-definition =
-Wno-old-style-definition -Wno-strict-prototypes --fatal-checks =
--timeout=3D0 --disable=3Dindex_overflow =
--disable=3Dsigned,all_func_returns -Wno-unused-variable =
-Wno-unused-value -Wno-unused-function -Wno-parentheses =
-Wno-maybe-uninitialized -Wno-clobbered -Wno-empty-body =
-fno-inline-small-functions -fno-inline-functions-called-once =
-fno-ipa-cp -fno-ipa-icf -fno-clone-functions -fno-reorder-functions =
-fno-reorder-blocks-and-partition -fno-aggressive-loop-optimizations =
--param=3Dmax-inline-insns-single=3D450 -fno-shrink-wrap =
-mindirect-branch=3Dthunk-extern -mindirect-branch-register =
-fno-asynchronous-unwind-tables -fstack-protector-strong =
-fno-eliminate-unused-debug-symbols -fno-eliminate-unused-debug-types =
-D_KERNEL -ffreestanding -D_SYSCALL32 -D_SYSCALL32_IMPL -D_ELF64 =
-D_DDI_STRICT -Dsun -D__sun -D__SVR4 -DOPTERON_ERRATUM_88 =
-DOPTERON_ERRATUM_91 -DOPTERON_ERRATUM_93 -DOPTERON_ERRATUM_95 =
-DOPTERON_ERRATUM_99 -DOPTERON_ERRATUM_100 -DOPTERON_ERRATUM_101 =
-DOPTERON_ERRATUM_108 -DOPTERON_ERRATUM_109 -DOPTERON_ERRATUM_121 =
-DOPTERON_ERRATUM_122 -DOPTERON_ERRATUM_123 -DOPTERON_ERRATUM_131 =
-DOPTERON_WORKAROUND_6336786 -DOPTERON_ERRATUM_147 -DOPTERON_ERRATUM_172 =
-DOPTERON_ERRATUM_298 -DOPTERON_ERRATUM_721 -I../../intel -nostdinc =
-I../../common -I/code/illumos-gate/usr/src/common =
-I/code/illumos-gate/usr/src/uts/common/fs/zfs -I../../i86pc -c -o =
/tmp/cwMia4d4.o ../../common/os/devcfg.c -mcmodel=3Dkernel=E2=80=99


and  for =E2=80=98pdip=E2=80=99 above, it did tell the user stack:

 10  87183            set_param_freed:entry=20
              smatch`set_param_freed
              smatch`call_db_return_callback+0x98
              smatch`db_return_states_callback+0x34c
              libsqlite3.so.3.50.4`sqlite3_exec+0x559
              smatch`sql_exec+0x1a1
              smatch`sql_select_return_states+0x15f
              smatch`db_return_states+0x9a
              smatch`db_return_states_call+0x59
              smatch`match_function_call+0x2e
              smatch`pass_expr_to_client+0x1f
              smatch`__pass_to_client+0xc8
              smatch`split_call+0x138
              smatch`__split_expr+0x4c5
              smatch`__split_stmt+0x335
              smatch`split_compound+0xca
              smatch`__split_stmt+0x346
              smatch`parse_fn_statements+0x24
              smatch`split_function+0x1c2
              smatch`split_c_file_functions+0x1d2
              smatch`smatch+0x17c

 10  87185               expr_to_str:return ndi_hold_devi(pdip)

 10  87185               expr_to_str:return pdip

Apparently we get =E2=80=98set_param_freed()=E2=80=99 called to mark =
=E2=80=9Cpdip=E2=80=9D freed from sql query, but we do not build sqllite =
db on disk - is it in memory db?

rgds,
toomas