Live data from Hacker News

Git: Introduce Rust and announce it will become mandatory in the build system

lore.kernel.org

421–430 of 433 posts

Re: Git: Introduce Rust and announce it will become mandatory in the build system

#421

Earlier quoted context omitted.

Sure, but I think illegal interprocess memory accesses is a fairly narrow definition for "access[ing] memory its not supposed to". There's plenty of undesirable memory accesses that are possible without needing to cross process boundaries and I don't think the OS does that much to solve those outside of currently niche hardware.

It might be undesirable to you, but you haven't specified this to the computer. Process-boundaries are one way how we specify what is allowed to touch and what not.

OK, sure, but there's no reason you can't extend that argument to in-process improper memory accesses either. free() is you specifying that a particular bit of memory isn't supposed to be touched any more, malloc() is you specifying that some amount of memory is legal to access, etc. Language runtimes, inserted/compile-time checks, etc. would be analogous to the OS/MMU here.

Re: Git: Introduce Rust and announce it will become mandatory in the build system

#422
post #419

Earlier quoted context omitted.

Hah.. C as too high level :) Thats weird.. Its cool to design sth from scratch, like its own CPU, but it only makes sense if you really enjoy it.. Except, its waste of time. I myself live in retro computing abit, but it have it limits. Like, I would not go back to prised 6502 CPU. Yeah, it was great, legendary CPU, but if we take a look at even cheap options we can buy and use for hobbie projects, there are plenty of…

It might become part of his diploma, so I don't think it's a waste of time. It's about non-binary logic without a shared clock. I understand nothing, but to me it sounds pretty cool.

Ah okey, different story :)

Re: Git: Introduce Rust and announce it will become mandatory in the build system

#423

Earlier quoted context omitted.

> My gut feeling is You're probably right, I was saying it just can be transferred, with a large "just". > the fact that neither GCC nor Clang appear to have discussed reusing concepts from their optimizer passes to recreate the borrow checker or borrow checker-like functionality There is however a commitment from GCC, that every UB the compiler exploits must be reported by -fanalyzer, otherwise it's a compiler bug.…

> I was saying it just can be transferred, with a large "just". I'm not really convinced it can be transferred at all , but I'm not convinced it can't be either. The "just" feels so large as effectively > There is however a commitment from GCC, that every UB the compiler exploits must be reported by -fanalyzer, otherwise it's a compiler bug. Huh, don't think I've heard that commitment before. Do you mean that the GCC…

> Huh, don't think I've heard that commitment before. Do you mean that the GCC devs intend for -fanalyzer to (eventually?) guarantee catching all exploitable UB (which would be... ambitious, to say the least), or that -fanalyzer is a best-effort analysis? The docs currently state the latter more or less ("It is neither sound nor complete: it can have false positives and false negatives.")

Both actually. Any UB exploits not catched by -fanalyzer would need to be disabled. However I can't find a reference to this, so maybe my memory is deceiving me.

When writing Frama-C what I was thinking of was actually PVS-Studio (https://pvs-studio.com/), as this can also be used by students. It's also more of a standalone linter.

>> It is up to the programmer to ensure that the lifetime of a `dependent` reference is contained within the lifetime of the corresponding `owned` reference.

Yes, this is an escape hatch, when the pointer shenanigans can't be fully described. But I heard Rust also has those. If you want your program to be described by ownership semantics, you will make use of this less and less.

> named lifetimes support

I don't know enough Rust, but from what I read at https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html, yes it does. Specifying that the 'lifetime' of the return value corresponds to a parameter happens with the `returned` annotation. The cool thing in SPlint is that you can describe the lifetime of param1.foo.bar[0..42] . It also has several types of 'lifetimes': allocated, readable and writable which is useful to represent uninitialized memory, meaning after a function call some stuff is newly uninitialized, that before the call wasn't. You also can combine this with parameters, so you can say that param1.baz[0..param2] is writable and param1.baz[0..param3] is readable and also that readable param1.baz[0..X] and writable param1.baz[0..Y] always means that X > Y.

It doesn't use the term 'lifetime', but talks about owned, allocated, initialized, readable and writable memory. In addition it also supports adding other properties, so much more then 'lifetimes' can be tracked. The manual shows as an example how it can be used to track variables that are tainted by user input (10.1). What I think is missing though, are conditionals on the return value.

How much of these features can be written in Rust? (Honest question)

> view struct support

I don't know really what these are. Maybe I already described that above?

Re: Git: Introduce Rust and announce it will become mandatory in the build system

#424
post #312
post #267

Earlier quoted context omitted.

Rust is a nice language, but it pushed too aggressively with the argument of "memory safety" at all cost ignoring other considerations. And Cargo is certainly a disaster even though it may be considered "fantastic tooling" by some. In any case, I do not think it is funny that I now depend on packages without timely security update in my distribution. This makes me less secure.

Is there better tooling in C/C++? No snark intended?

There are tons of package managers. They just don't exclude other languages besides C and C++. There are also build systems en mass. Some even were included into an inter-OS standard.

Re: Git: Introduce Rust and announce it will become mandatory in the build system

#425

Earlier quoted context omitted.

It might be undesirable to you, but you haven't specified this to the computer. Process-boundaries are one way how we specify what is allowed to touch and what not.

OK, sure, but there's no reason you can't extend that argument to in-process improper memory accesses either. free() is you specifying that a particular bit of memory isn't supposed to be touched any more, malloc() is you specifying that some amount of memory is legal to access, etc. Language runtimes, inserted/compile-time checks, etc. would be analogous to the OS/MMU here.

Yes, but this is not across a trust boundary. Since these are in the same process/program. Rust "only" applies checks at compile time, it doesn't enforce security.

Not sure, if I'm clear. Rust is like cooperative multitasking, nice but not guaranteed. My claim here is, that we actually want preemptive multitasking.

Re: Git: Introduce Rust and announce it will become mandatory in the build system

#426

Earlier quoted context omitted.

> I was saying it just can be transferred, with a large "just". I'm not really convinced it can be transferred at all , but I'm not convinced it can't be either. The "just" feels so large as effectively > There is however a commitment from GCC, that every UB the compiler exploits must be reported by -fanalyzer, otherwise it's a compiler bug. Huh, don't think I've heard that commitment before. Do you mean that the GCC…

> Huh, don't think I've heard that commitment before. Do you mean that the GCC devs intend for -fanalyzer to (eventually?) guarantee catching all exploitable UB (which would be... ambitious, to say the least), or that -fanalyzer is a best-effort analysis? The docs currently state the latter more or less ("It is neither sound nor complete: it can have false positives and false negatives.") Both actually. Any UB exploi…

> Both actually.

That would be quite the surprise to me. Quite unfortunate that you can't find a source given what the -fanalyzer docs currently say.

> Any UB exploits not catched by -fanalyzer would need to be disabled.

I'd be curious as to the hypothetical performance impact of this, as well as the amount of work it'd take to make -fanalyzer reliable enough.

> When writing Frama-C what I was thinking of was actually PVS-Studio (https://pvs-studio.com/), as this can also be used by students. It's also more of a standalone linter.

Ah, that's quite different.

> Yes, this is an escape hatch, when the pointer shenanigans can't be fully described.

Ah, that's fair. Bit different than what Rust offers, but what you say makes sense.

> But I heard Rust also has those.

Sort of yes, sort of no. Rust has an escape hatch in `unsafe`, but it technically doesn't disable any checks - for example, the borrow checker will check the validity of references, bounds checks will continue to be inserted, etc. regardless of whether you're in an `unsafe` block or not. What it does instead is to give you the ability to perform `unsafe` operations, which for borrow checker-related shenanigans would typically involve dealing with pointers (to be specific, dereferencing them since some other pointer operations are considered safe) since the borrow checker doesn't check pointers.

> I don't know enough Rust, but from what I read at https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html, yes it does. Specifying that the 'lifetime' of the return value corresponds to a parameter happens with the `returned` annotation.

That's one of the things named lifetimes let you do, but named lifetimes are flexible enough for more than just that use case, from slightly more complex things like dealing with multiple independent lifetimes at the same time (for example, returning two references instead of just one, or structs/functions using multiple lifetimes), to more obscure stuff like dealing with higher-ranked trait bounds [1].

Kind of a tangent, but the manual is a bit unclear to me as to what `returned` is capable of. It states "The returned annotation denotes a parameter that may be aliased by the return value.", but that's immediately followed by "Splint checks the call assuming the result may be an alias to the returned parameter." And later in the example it states "Because of the `returned` qualifier, Splint assumes the result of `intSet_insert` is the same storage as its first parameter, in this case the storage returned by `intSet_new`."

Does `returned` require that the annotated parameter correspond exactly to the return value, or can a "subset" of the parameter be returned? In more concrete terms, would something like the following be accepted (assuming the access is in bounds, of course)?

    int *get_fifth(/*@returned*/ int *slice) {
        return &slice[4];
    }
What about instances where the lifetime of the output may be tied to multiple parameters? For example, consider the following Rust function:

    fn max(a: &'a i32, b: &'a i32) -> &'a i32 {
        if a > b { a } else { b }
    }
My naive attempt at a translation to Splint would be:

    int *max(/*@returned*/ int *a, /*@returned*/ int *b) {
        if (*a > *b) { return a; } else { return b; }
    }
Would that be accepted as well?

In general, though, it does seem that Splint is able to describe many common patterns the borrow checker is also able to cover. I suspect the differences (in both directions) are probably only going to emerge for more complex use cases.

> You also can combine this with parameters, so you can say that param1.baz[0..param2] is writable and param1.baz[0..param3] is readable and also that readable param1.baz[0..X] and writable param1.baz[0..Y] always means that X > Y.

To be honest I don't quite follow, but I would guess that Rust isn't capable of anything similar since there isn't a way to describe properties for a subset of a slice in signatures.

> How much of these features can be written in Rust? (Honest question)

Assuming I'm understanding these correctly:

- owned: Typically represented via Box or plain non-reference types.

- allocated, initialized, readable, writable: I believe these are generally handled via MaybeUninit [0] because everything is otherwise assumed to be properly initialized. Readable/writable might need &/&mut on top of MaybeUninit.

- Other properties: Might depend on the exact properties, but I think stuff like tainted input would usually be represented directly in the type system - in this example, taintedness would be types and transitions would be functions. There are at least two ways to implement that that I can think of right now. The first is via a simple newtype (might have syntax errors, but the gist should be clear):

    struct SafeUserString(String);
    struct UnsafeUserString(String);
    fn validate(s: UnsafeUserString) -> Result {
        if s.0.contains("/") {
            Err(ValidationError::InvalidChar)
        } else {
            Ok(SafeUserString{s.0})
        }
    }
The other is via constrained type parameters + PhantomData:

    trait Safety {}
    struct Safe;
    impl Safety for Safe {}
    struct Unsafe;
    impl Safety for Unsafe {}
    struct UserString { data: String }
    fn validate(input: UserString) -> Result, ValidationError> {
        // ...
    }
    fn op_that_does_not_care_about_safety(input: UserString) -> UserString {
        // ...
    }
> I don't know really what these are. Maybe I already described that above?

Basically a struct that contains a reference into data something else owns. Something like:

    struct ByteSlice {
        slice: &'a [u8]
    }
Similar patterns are quite common for iterator structs as well.

[0]: https://doc.rust-lang.org/beta/std/mem/union.MaybeUninit.htm...

[1]: https://doc.rust-lang.org/nomicon/hrtb.html

Re: Git: Introduce Rust and announce it will become mandatory in the build system

#427

Earlier quoted context omitted.

> Huh, don't think I've heard that commitment before. Do you mean that the GCC devs intend for -fanalyzer to (eventually?) guarantee catching all exploitable UB (which would be... ambitious, to say the least), or that -fanalyzer is a best-effort analysis? The docs currently state the latter more or less ("It is neither sound nor complete: it can have false positives and false negatives.") Both actually. Any UB exploi…

> Both actually. That would be quite the surprise to me. Quite unfortunate that you can't find a source given what the -fanalyzer docs currently say. > Any UB exploits not catched by -fanalyzer would need to be disabled. I'd be curious as to the hypothetical performance impact of this, as well as the amount of work it'd take to make -fanalyzer reliable enough. > When writing Frama-C what I was thinking of was actuall…

> That's one of the things named lifetimes let you do, but named lifetimes are flexible enough for more than just that use case, from slightly more complex things like dealing with multiple independent lifetimes at the same time (for example, returning two references instead of just one, or structs/functions using multiple lifetimes), to more obscure stuff like dealing with higher-ranked trait bounds [1].

In SPlint you would describe something as `special` and then describe the individual elements.

> Kind of a tangent, but the manual is a bit unclear to me as to what `returned` is capable of.

Aliasing is mutual and storage refers to the region an object lives. It doesn't refer to exact pointer values.

> Would that be accepted as well?

    $ cat test.c

    int *
    max (/*@returned@*/ int * a, /*@returned@*/ int * b) {
     if (*a > *b) return a; else return b;
    }
    
    int
    main (void)
    {
     int * a = malloc (sizeof *a);
     int * b = malloc (sizeof *b);
     int * c;
    
     if (!a || !b) abort ();
    
     *a = 4;
     *b = 6;
    
     c = max (a, b);
    
     free (a);
     free (b);
     free (c);
     return 0;
    }

    $ splint test.c

    Splint 3.1.1 --- 05 Jan 2023

    Finished checking --- 2 code warnings
    test.c: (in function main)
    test.c:22:8: Dead storage c passed as out parameter to free: c
      Memory is used after it has been released (either by passing as an only param
      or assigning to an only global). (Use -usereleased to inhibit warning)
       test.c:20:8: Storage c released
    test.c:2:1: Function exported but not used outside test: max
      A declaration is exported, but not used outside this module. Declaration can
      use static qualifier. (Use -exportlocal to inhibit warning)
       test.c:4:1: Definition of max

Re: Git: Introduce Rust and announce it will become mandatory in the build system

#428

Earlier quoted context omitted.

> Both actually. That would be quite the surprise to me. Quite unfortunate that you can't find a source given what the -fanalyzer docs currently say. > Any UB exploits not catched by -fanalyzer would need to be disabled. I'd be curious as to the hypothetical performance impact of this, as well as the amount of work it'd take to make -fanalyzer reliable enough. > When writing Frama-C what I was thinking of was actuall…

> That's one of the things named lifetimes let you do, but named lifetimes are flexible enough for more than just that use case, from slightly more complex things like dealing with multiple independent lifetimes at the same time (for example, returning two references instead of just one, or structs/functions using multiple lifetimes), to more obscure stuff like dealing with higher-ranked trait bounds [1]. In SPlint y…

> In SPlint you would describe something as `special` and then describe the individual elements.

Interesting. That does seem to provide more flexibility, though based on what the manual says I still feel like it's not quite to the same level as named lifetimes since `special` looks like it revolves around allocation/initialization?

At least based on a quick skim of the manual structs with non-owning pointers still seems like a potential difference since from what I can tell struct field checks are either for ownership (/@only@/ for fields by default), initialization/allocation (`partial`, state clauses), or requires overhead (/@refs@/). Nothing quite like "the data here will live for some arbitrary lifetime(s) dictated by the use context".

> Aliasing is mutual and storage refers to the region an object lives. It doesn't refer to exact pointer values.

I... think that answers my question? Some quick tests seem to bear that out as well.

> $ cat test.c

So I think I'm a bit dim and for some reason I thought Splint was not free. Sorry for the bother! I could have tried things out myself this entire time!

Good to see that /@returned@/ works like I hoped at least.

Re: Git: Introduce Rust and announce it will become mandatory in the build system

#429

Earlier quoted context omitted.

OK, sure, but there's no reason you can't extend that argument to in-process improper memory accesses either. free() is you specifying that a particular bit of memory isn't supposed to be touched any more, malloc() is you specifying that some amount of memory is legal to access, etc. Language runtimes, inserted/compile-time checks, etc. would be analogous to the OS/MMU here.

Yes, but this is not across a trust boundary. Since these are in the same process/program. Rust "only" applies checks at compile time, it doesn't enforce security. Not sure, if I'm clear. Rust is like cooperative multitasking, nice but not guaranteed. My claim here is, that we actually want preemptive multitasking.

I'm not quite sure I'm understanding your analogy here, but would that effectively mean each allocation lives in its own process?

Re: Git: Introduce Rust and announce it will become mandatory in the build system

#430

Earlier quoted context omitted.

> That's one of the things named lifetimes let you do, but named lifetimes are flexible enough for more than just that use case, from slightly more complex things like dealing with multiple independent lifetimes at the same time (for example, returning two references instead of just one, or structs/functions using multiple lifetimes), to more obscure stuff like dealing with higher-ranked trait bounds [1]. In SPlint y…

> In SPlint you would describe something as `special` and then describe the individual elements. Interesting. That does seem to provide more flexibility, though based on what the manual says I still feel like it's not quite to the same level as named lifetimes since `special` looks like it revolves around allocation/initialization? At least based on a quick skim of the manual structs with non-owning pointers still se…

> At least based on a quick skim of the manual structs with non-owning pointers still seems like a potential difference since from what I can tell struct field checks are either for ownership (/@only@/ for fields by default), initialization/allocation (`partial`, state clauses), or requires overhead (/@refs@/). Nothing quite like "the data here will live for some arbitrary lifetime(s) dictated by the use context".

Then I don't understand (your/Rusts) understanding of lifetimes. As to my understanding, the lifetime of an object is bound by the lifetime of the underlying allocation and is the time during which the storage of the allocation is initialized without interruption.

> I thought Splint was not free

It is for example in Debian, but I have recompiled it myself, since the Debian version has some bugs.

Post reply on HN