While experimenting with AutoCorres we noticed something unexpected in the way the C Parser treats local uninitialised variables when translating to SIMPL. The concrete examples in AutoCorres and AutoCorres2 are bellow.
Example in AutoCorres 1.12
In this example we are using AutoCorres 1.12 with Isabelle2025 and L4V_ARCH=ARM environment variable. Consider the following function:
int bogus(void) {
int x;
if (x < 100) {
return 0;
} else {
return 1;
}
}
If we import this using AutoCorres, we see by inspecting bogus'_def that the abstracted definition of bogus function becomes:
bogus' ≡ do x___int <- unknown;
return (if x___int < 100 then 0 else 1)
od
Where unknown is a nondeterministic selector from all integers. As far as we understand, the function in C has undefined behaviour as the local variable x is not initialised and it is accessed in the if statement. Because of this, we would expect that we should not be able to reason about this program. However, with the abstracted definition above we can prove that the function returns 0 or 1:
theory Bogus
imports
"AutoCorres.AutoCorres"
begin
external_file "bogus.c"
install_C_file "bogus.c"
thm Bogus.bogus_global_addresses.bogus_body_def
autocorres "bogus.c"
context bogus begin
thm bogus'_def
lemma bogus_res:
"⦃ λs. True ⦄
bogus'
⦃ λr _. r = 0 ∨ r = 1 ⦄!"
apply (unfold bogus'_def)
apply wp
by meson
end
end
We can see that the result of translating C to SIMPL is this:
bogus_global_addresses.bogus_body ≡
TRY
lvar_nondet_init x_' x_'_update;;
IF ´x <s 0x64 THEN
creturn global_exn_var_'_update ret__int_'_update (λs. 0)
ELSE
creturn global_exn_var_'_update ret__int_'_update (λs. 1)
FI;;
Guard DontReach {} SKIP
CATCH SKIP
END
x seems to be initialised as a nondeterministic variable. We would expect there to be some guards before IF statement that prevent branching on an uninitialised value.
Same example in AutoCorres2
We originally spotted this using AutoCorres2 as provided in AFP 2025-12-27 version and Isabelle 2025-1.
Abstracted definition of the function bogus:
bogus' ≡ do {
x ← unknown;
return (if x < 100 then 0 else 1)
}
Proof that it returns 0 or 1:
theory Bogus
imports
"AutoCorres2_Main.AutoCorres_Main"
begin
install_C_file "bogus.c"
thm bogus_body_def
autocorres "bogus.c"
thm bogus'_def
lemma bogus_res:
"bogus' ∙ s ⦃ λRes r _. r = 0 ∨ r = 1 ⦄"
unfolding bogus'_def
by runs_to_vcg
end
Result of the translation to SIMPL:
bogus_body ≡
TRY
lvar_nondet_init (λupd. bogus.ret':=⇩ℒ upd);;
(lvar_nondet_init (λupd. bogus.x:=⇩ℒ upd);;
IF ´bogus.x <s 0x64 THEN
creturn global_exn_var'_'_update (λupd. bogus.ret':=⇩ℒ upd) (λs. 0)
ELSE
creturn global_exn_var'_'_update (λupd. bogus.ret':=⇩ℒ upd) (λs. 1)
FI);;
Guard DontReach {} SKIP
CATCH ccatchreturn global_exn_var'_'
END
Questions
We would like to ask:
- Is this a known issue?
- Are there some additional assumptions about the C semantics, target compilers, or architectures where such treatment of locals is valid?
- Is there a way that the C Parser could be configured to add guards to disallow such UB?
- Do you have a strategy for dealing with such programs?
One naive patch maybe could be to disallow declarations of locals without assigning value. E.g. forbidding statements like int x; but allowing int x = 0; in function bodies in the C subset of C Parser. Though we don't know if this could have some larger implications, e.g. while dealing with structs that are locals or regarding efficiency. For our use case we could perhaps circumvent this in a way by using some linter on the C source to detect such patterns.
While experimenting with AutoCorres we noticed something unexpected in the way the C Parser treats local uninitialised variables when translating to SIMPL. The concrete examples in AutoCorres and AutoCorres2 are bellow.
Example in AutoCorres 1.12
In this example we are using AutoCorres 1.12 with Isabelle2025 and
L4V_ARCH=ARMenvironment variable. Consider the following function:If we import this using AutoCorres, we see by inspecting
bogus'_defthat the abstracted definition ofbogusfunction becomes:Where
unknownis a nondeterministic selector from all integers. As far as we understand, the function in C has undefined behaviour as the local variablexis not initialised and it is accessed in theifstatement. Because of this, we would expect that we should not be able to reason about this program. However, with the abstracted definition above we can prove that the function returns 0 or 1:We can see that the result of translating C to SIMPL is this:
xseems to be initialised as a nondeterministic variable. We would expect there to be some guards beforeIFstatement that prevent branching on an uninitialised value.Same example in AutoCorres2
We originally spotted this using AutoCorres2 as provided in AFP 2025-12-27 version and Isabelle 2025-1.
Abstracted definition of the function
bogus:Proof that it returns 0 or 1:
Result of the translation to SIMPL:
Questions
We would like to ask:
One naive patch maybe could be to disallow declarations of locals without assigning value. E.g. forbidding statements like
int x;but allowingint x = 0;in function bodies in the C subset of C Parser. Though we don't know if this could have some larger implications, e.g. while dealing with structs that are locals or regarding efficiency. For our use case we could perhaps circumvent this in a way by using some linter on the C source to detect such patterns.