News for hackers. Stuff that matters.
Re:The C Bounded Model Checker: Criminally Underused by philzook on Tuesday January 30, 2024 @03:09PM (#39195040)
Your example does not pass the verifier

   cbmc /tmp/walter.c --bounds-check --pointer-check --function test

   ...
   [get.pointer_dereference.5] line 3 dereference failure: pointer outside object bounds in buffer[(signed long int)10]: FAILURE
   ...
There are many footguns. People love their guns. Makes them feel powerful.
Re:The C Bounded Model Checker: Criminally Underused by WalterBright on Tuesday January 30, 2024 @03:40PM (#39195485)

    int get(char* buffer, int i)
    {
        return buffer[i];
    }

    #include <stdio.h>

    void test() {
        char buffer[10];
        get(buffer, getc(stdin));
    }
Re:The C Bounded Model Checker: Criminally Underused by nanolith on Tuesday January 30, 2024 @03:51PM (#39195629)
CBMC's default contract for getc returns a non-deterministic integer value. A non-deterministic integer value basically counts as "pick a value that would cause this de-reference to crash the machine model".

So, it will find a counter-example.

Re:The C Bounded Model Checker: Criminally Underused by philzook on Tuesday January 30, 2024 @03:54PM (#39195681)
Yes, it returned the same "FAILURE" along with a couple new checks about getc that do pass.
Re:The C Bounded Model Checker: Criminally Underused by WalterBright on Tuesday January 30, 2024 @04:45PM (#39196309)
Sounds like it's doing a solid job of data flow analysis. But I can still produce cases where it cannot determine either the range of the index or the length of the array being pointed to.

The programmer will have to add code to keep track of the length of the array, and add code to check the value of the index. The good thing is the analyzer will tell you where you'll need to add those checks.

With the proposal I made, none of this is necessary. It just works. Over two decades of experience with this approach in D is it-just-works.

Re:The C Bounded Model Checker: Criminally Underused by nanolith on Tuesday January 30, 2024 @04:49PM (#39196352)
I'm curious, have you used CBMC?

The machine model tracks the array with its size and compares this to any index. This is loaded into an SMT solver. This is not a standard static analyzer. It's compiling the code to an abstract machine model and then running through this model with an SMT solver.

CBMC isn't perfect, but the strategies you are talking about are meant to defeat a linter or a shallow analyzer in a compiler. CBMC isn't a linter.

Re:The C Bounded Model Checker: Criminally Underused by WalterBright on Tuesday January 30, 2024 @05:41PM (#39196951)
I have never heard of CBMC before this thread.

SMT cannot solve cases where it simply cannot know what the length of an array is. For example, when array is allocated in code not available to the solver, or is set by the environment.

I remember trying out Spark a few years ago, which advertised its compile time checking abilities. I tried using an integer equation that used an OR, and it gave up on it.

If you need to add in range checking code to help the solver along, then the range check itself is a source of bugs, and the range limit has to be made available somewhere and be correct.

In my proposal, the array length is set at the time of creation of the array, and the length is carried along with the pointer. The solver's role then becomes one of eliminating the bounds check in the cases where it can prove the index is within range. The user doesn't have to do anything.

We've been using it in the implementation of the D compiler for a very long time now, and problems with buffer overflows are a faded memory.

P.S. I also added manual integer overflow checks when passing the size to malloc(), no matter how unlikely an overflow would be :-)

Re:The C Bounded Model Checker: Criminally Underused by nanolith on Tuesday January 30, 2024 @06:01PM (#39197181)
> SMT cannot solve cases where it simply cannot know what the length of an array is.

Sure it can. It sets the array length to a non-deterministic unsigned integer, and then finds a counter-example where this array length is invalid. CBMC will also make the array pointer itself non-deterministic if you haven't refined it. So, the pointer could be NULL, could be pointing to invalid memory, etc.

Don't think of values as fixed numbers, but rather as functions shaping a non-deterministic value. The range check made upstream becomes part of this function. The goal of the SMT solver is to find a counter-example that crashes the machine model. If you want a rather leaky abstraction, imagine that by transforming the source code into an SMT equation, it's effectively turning it inside out and transforming it into a logic equation with modulo math.

I agree with you that it would be nice if bounds were included in C arrays. But, we have to work with what we have. Unfortunately, especially in firmware that requires a commercial C compiler, we are stuck with C. In those cases, if we want safer software, we need tooling like this.

Re:The C Bounded Model Checker: Criminally Underused by WalterBright on Tuesday January 30, 2024 @06:52PM (#39197722)
Thank you for the explanation.

I agree that if one isn't going to enhance C, one is going to have to resort to these tools.

C gets new features now and then. Why not add something incredibly useful, like the slice proposal? Instead, C23 got enhanced with the crazy Unicode identifiers. Richard Cattermole has been adding them to D's C support, requiring 6000 lines of code!!

https://github.com/dlang/dmd/pull/15307

The entire C parser is 6000 lines of code:

https://github.com/dlang/dmd/blob/master/compiler/src/dmd/cp...

Re:The C Bounded Model Checker: Criminally Underused by nanolith on Tuesday January 30, 2024 @07:25PM (#39198017)
Yeah... I'm not too happy with some of the choices made in C18 and C23. One of the reasons why I chose to standardize my C SAX-like parser on C18 for now was to avoid the Unicode complexity. There's also a widening feature gap between modern C and modern C++ that makes interop harder.
Re:The C Bounded Model Checker: Criminally Underused by lifthrasiir on Tuesday January 30, 2024 @11:56PM (#39200112)
> Richard Cattermole has been adding them to D's C support, requiring 6000 lines of code!!

I wondered if you were implementing a normalization (which is complicated), but that doesn't seem the case? I have read the PR and it's clear that it solves multiple issues at once: no start-continue distinction (bug), a code generator, the table itself (which is just an inefficiently formatted block of data). 6000 lines of code just for Unicode identifiers are simply an exaggeration.

Re:The C Bounded Model Checker: Criminally Underused by WalterBright on Wednesday January 31, 2024 @02:37AM (#39201116)
There are different ways to count lines. I included in the C parser count the comment lines.

6000 is also a round number, I did that deliberately to imply the number was not exact.

Re:The C Bounded Model Checker: Criminally Underused by lifthrasiir on Wednesday January 31, 2024 @03:56AM (#39201580)
A new generated code alone is 4000 lines long [1]. The actual code added is just 2000 lines, and some are used to pay debts, I mean, to make a proper code generator. It is debatable whether that generator should count in the number of lines, since you can always write a generator in a simpler scripting language (for example, Rust uses Python to generate the `unicode-xid` crate). In any case however it is far less complex and, in my opinion, much smaller than the entire C parser proper.

[1] https://github.com/dlang/dmd/pull/15307/files#diff-3677bcc89...

Re:The C Bounded Model Checker: Criminally Underused by mhh__ on Wednesday January 31, 2024 @05:56PM (#39210602)
Most big PRs are only big because pull requests are a fundamentally bad model for doing work that's already been planned.
Re:The C Bounded Model Checker: Criminally Underused by rurban on Wednesday January 31, 2024 @01:52AM (#39200810)
> C23 got enhanced with the crazy Unicode identifiers.

Actually, this spec security bug was added with C11. But nobody really added it, until gcc decided that this new "feature" needs to be supported around C23 time. C23 tried to mitigate that mess by disallowing non-identifiable normalization variants, but still does not care about Unicode security guidelines.

Re:The C Bounded Model Checker: Criminally Underused by mhh__ on Wednesday January 31, 2024 @05:54PM (#39210563)
A lot of static analysis tools explicitly support forcing you to check the value of "tainted" values such as return values of getc and so on i.e. even with slices you want to be able to catch this kind of thing ahead of time.
Re:The C Bounded Model Checker: Criminally Underused by Joker_vD on Tuesday January 30, 2024 @05:34PM (#39196855)
> There are many footguns. People love their guns. Makes them feel powerful.

Well, duh: if you remove the most egregious footguns from C, you end up basically with Pascal, and here is Why Pascal Is Not My Favorite Programming Language.

And when you try to explain to them that C's abstract machine has semantics of "when you omit a safety check, you actually makes a promise to the compiler that this check is redundant, that you'll promise that you'll make sure in some other way that in no possible execution will the illegal values ever show up in this place, and the compiler will actually hold you to this promise" — they throw tantrums and try to argue that no, it hasn't this semantics, and even if has, it's not the semantics as originally intended by K&R and it is the authors of the C compilers who are wrong (even though the authors of C compilers are the people who actually write both the C standards and the C compilers...)

Must have to do something with imprintment: I've learned C as my 3rd language, and from the start I was taught that its UB is absolutely insane — and I've never felt "nah, it should just do what I meant, dangit" denial about it, only "this is sad, how can I even know while writing code when I am making a baseless promise especially when it's made by omitting something?" But like you've said, people like their footguns.

Re:The C Bounded Model Checker: Criminally Underused by GuestHNUser on Wednesday January 31, 2024 @03:21AM (#39201367)
> Must have to do something with imprintment

Eh, I think most C programmers frustrations with UB stem from knowing that the C standard has fundamental flaws and modern compilers abuse that fact for "optimizations" on UB. This paper covers the topic pretty well[0].

I fully support Casey Muratori's viewpoint that undefined behavior should not exist in the standard[1]. Instead, the C standard should enumerate all valid behaviors compliant compilers can implement. This would allow compilers to make unintuitive optimizations for the platforms that need them, but still allow programmers to be certain that their program semantics will not change in different versions of the said compiler.

[0] https://www.complang.tuwien.ac.at/kps2015/proceedings/KPS_20... [1] https://youtu.be/dyI0CwK386E?si=vsqJ8uWHY8xkGmFm

Re:The C Bounded Model Checker: Criminally Underused by Joker_vD on Wednesday January 31, 2024 @05:44AM (#39202238)
> I think most C programmers frustrations with UB stem from knowing that the C standard has fundamental flaws and modern compilers abuse that fact for "optimizations" on UB

Do they actually know that? I don't think so. Let's take [0] for instance (the author is an expreienced C programmer who loves the language and wrote a re-implementation of bc in it):

    There seemed to be a lot of misunderstandings; I could not get a handle on what this person thought UB meant.

    I finally figured it out: this person’s definition of UB was not “the language spec can’t guarantee anything.” Instead, it was “compilers can assume UB does not exist and optimize accordingly.”

    Wat.
Yep. Apparently, that was news to him, even though C implementations have been behaving like that for about 30 years already, and you can read the rest of the post for the "this is evil, we the users must do something about it" take. And the proposed "something" is not "we should instead use a language with actually defined semantics", oh no. It's "use compiler flags to force more reasonable behaviour, hopefully" and "somebody should write boringcc, unfortunately, I am myself a bit too busy for that". Well, despite numerous pleas and several attempts, nobody has managed to write boringcc which is telling of something, I'm just not sure of what exactly. So... I think it is about imprinting: "Oh, it's a wonderful language, well, it would be if it was actually implemented the way I used to think it is implemented (and I still think it should be implemented that way) but still, it's a wonderful language if only not for that pesky realitiy" is forcing unfounded expectations onto reality, and where do these expectations even come from in the first place?

And yeah, I fully agree with you that C standard should've probably done that. But it didn't happen, and it can't happen because backwards compatibility [1]. But even then, C programs would still be non-portable, in a sense that you have to use #ifdef's for tinkering with platform-specific behaviour for anything interesting, because C standard even today leaves a lot of stuff completely up to implementation, see [2] for an especially apalling example, even without touching UB.

[0] https://gavinhoward.com/2023/08/the-scourge-of-00ub/

[1] https://thephd.dev/your-c-compiler-and-standard-library-will...

[2] https://thephd.dev/conformance-should-mean-something-fputc-a...

Re:The C Bounded Model Checker: Criminally Underused by aw1621107 on Wednesday January 31, 2024 @07:38AM (#39203020)
> Well, despite numerous pleas and several attempts, nobody has managed to write boringcc which is telling of something, I'm just not sure of what exactly.

John Regehr has a blog post that touches upon why this is the case [0]:

> I’ll assume you’re familiar with the Proposal for Friendly C and perhaps also Dan Bernstein’s recent call for a Boring C compiler. Both proposals are reactions to creeping exploitation of undefined behaviors as C/C++ compilers get better optimizers. In contrast, we want old code to just keep working, with latent bugs remaining latent.

> After publishing the Friendly C Proposal, I spent some time discussing its design with people, and eventually I came to the depressing conclusion that there’s no way to get a group of C experts — even if they are knowledgable, intelligent, and otherwise reasonable — to agree on the Friendly C dialect. There are just too many variations, each with its own set of performance tradeoffs, for consensus to be possible.

Granted, at the end he says that it's not that it's impossible, it's just not for him, so it's still possible that someone else would succeed, but they'd have an uphill battle for sure.

[0]: https://blog.regehr.org/archives/1287

Re:The C Bounded Model Checker: Criminally Underused by uecker on Wednesday January 31, 2024 @04:40PM (#39209786)
Those ideas are not convincing. Consider the glibc bug from yesterday. If I understood it correctly, there is an integer overflow causing a heap overflow. The integer overflow was unsigned wraparound - so fully defined behavior. So no, we do not want latent bugs remain latent. We want UB sanitizers and checkers and fix all this crap.
Re:The C Bounded Model Checker: Criminally Underused by aw1621107 on Wednesday January 31, 2024 @05:37PM (#39210413)
I think that's arguably a distinct concern from what boringcc/friendly C are intended to address. My understanding is that the latent bugs those are intended to keep are those that would be unearthed by aggressive exploitation of UB. Sanitizers/checkers/etc. would still be useful iirc boringcc/friendly C for finding bugs arising out of fully-defined behavior.
Re:The C Bounded Model Checker: Criminally Underused by uecker on Sunday February 04, 2024 @02:56PM (#39253751)
Sanitizers / checkers are much useful if the behavior is defined, because you can then not be sure it is not intentionally used in the way allowed by the definition. So you can not check at run-time in production and stop the program but you can with UB and during testing you would need to analyze each case individually.
Re:The C Bounded Model Checker: Criminally Underused by aw1621107 on Monday February 05, 2024 @07:53PM (#39269365)
I think sanitizer usefulness for defined behaviors is going to depend a lot on how frequently those behaviors are intentionally used and how easy/hard it would be to suppress false positives.

There might be some behaviors that sanitizers don't currently catch but are used for optimizations as well, and boringcc/friendly C might be useful for those? Not entirely sure what those might be, if they even exist, though; IIRC sanitizers don't currently flag strict aliasing violations, but -fno-strict-aliasing exists so there's no need for a new compiler/dialect (for GCC/Clang?)

Re:The C Bounded Model Checker: Criminally Underused by jiggawatts on Wednesday January 31, 2024 @07:07AM (#39202809)
Wow, those linked blog posts are... eye-opening.
Re:The C Bounded Model Checker: Criminally Underused by uecker on Wednesday January 31, 2024 @03:22PM (#39208855)
Actually the definition of UB is not "compilers can assume UB does not exist" but indeed "the language spec can't guarantee anything". We clarified this in the C23 spec. (although a limited form of "compilers can assume UB does not exist" can be derived from "the spec can't guarantee anything")
Re:The C Bounded Model Checker: Criminally Underused by Joker_vD on Thursday February 01, 2024 @06:52AM (#39214990)
Well, I was under impression that word "anything" actually means "anything", so e.g. "the language spec can't guarantee that the compilers won't assume that the execution paths with UB on them never happen and then use that assumption for optimization" is a valid instantiation of "the language spec can't guarantee anything".
Re:The C Bounded Model Checker: Criminally Underused by uecker on Friday February 02, 2024 @12:58AM (#39225582)
Actually, the spec does not say exactly "the language spec can't guarantee anything". It says "undefined behavior --- behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this document imposes no requirements." So it says "no requirements" but this refers specifically ("for which") to the behavior in question and not the whole program. This implies that preceding observable behavior can not be affected. (but non-observable behavior can according to the "as-if" rule and this is sufficient for optimization). We added a note to clarify this in C23.
Re:The C Bounded Model Checker: Criminally Underused by gavinhoward on Thursday February 01, 2024 @10:43PM (#39224741)
As the author of the first blog post, I think you got my conclusion wrong because I'm writing a language without such UB. So I would like to have a better language.

But I was also talking about UB in the context of already existing code! My argument is that compiler writers are breaking existing code when they could very well avoid breaking it.

We have tried to convince compiler writers to not do this, but they have refused. So that's why I said that users must do something: because compiler writers won't.

And yes, I knew compilers would take every advantage that they could before that; my surprise was that someone considered UB's sole purpose to be for the benefit of compiler writers at the expense of everyone else, including non-programmers who suffer catastrophic consequences for security bugs.

Re:The C Bounded Model Checker: Criminally Underused by pjmlp on Wednesday January 31, 2024 @05:47AM (#39202252)
Pascal, the dialects most people used, not the ISO Pascal from 1972 used by Kernighan, had ways to pass around dynamically size arrays and strings.

Which anyway was not an issue in the PL/I variants that predated C, nor the Mesa/Modula-2 descendents from the mid-70's.

EDIT:

Walter's example with 1978's Modula-2.

    MODULE Example;

    FROM InOut IMPORT WriteString, Write, WriteLn;

    PROCEDURE get(buffer : ARRAY OF CHAR): CHAR;
    BEGIN
      RETURN buffer[10]; (* This will boom unless compiled with bounds checking disabled, in which case who knows *)
    END get;

    VAR
      buffer : ARRAY [0..9] OF CHAR;

    BEGIN
        WriteString('The out of bounds character is');
        Write(get(buffer));
        WriteLn;
    END Example.
Re:The C Bounded Model Checker: Criminally Underused by Joker_vD on Wednesday January 31, 2024 @05:52AM (#39202292)
Yes, and most Pascal implementations (and the Pascal descendants) also have functioning module systems, unlike C. But worse is better.
Re:The C Bounded Model Checker: Criminally Underused by pjmlp on Wednesday January 31, 2024 @06:07AM (#39202391)
It is more like, having a free beer OS to come alongside the language is a big selling point.
Re:The C Bounded Model Checker: Criminally Underused by WalterBright on Wednesday January 31, 2024 @11:21AM (#39205601)
ImportC has a functioning module system! It's a consequence of it being built alongside a D compiler.