[HN Gopher] Cve-rs: Fast memory vulnerabilities, written in safe...
___________________________________________________________________
Cve-rs: Fast memory vulnerabilities, written in safe Rust
Author : npalli
Score : 224 points
Date : 2024-02-20 13:17 UTC (9 hours ago)
(HTM) web link (github.com)
(TXT) w3m dump (github.com)
| WirelessGigabit wrote:
| It's weird to see lifetime correlations expressed as
| &'a &'b
|
| and not with where 'a: 'b
|
| I suppose I don't understand enough of how the first is different
| from the second one, and how it impacts this issue.
| dannymi wrote:
| It's just a reference to a reference. It has nothing to do with
| lifetime correlations.
|
| Also, it's a lot less weird if you don't stop in the middle of
| the type. The entire type is for example &'a
| &'b u8
|
| or &'a &'b ()
|
| So the outer reference has lifetime 'a and the inner reference
| has lifetime 'b.
| Georgelemental wrote:
| > It has nothing to do with lifetime correlations
|
| Yes it does; there's an implied `'b: 'a` outlives
| relationship that's required for the type to be well-formed.
| kbknapp wrote:
| In fact, I believe that's exactly where this bug lies. You're
| effectively able to trigger a case in which passing `&'a &'b`
| _without_ providing any correlation (`where 'a: 'b`) that one
| would normally be required to provide makes the compiler behave
| as if those correlations were passed, albeit inferred
| incorrectly.
| LegionMammal978 wrote:
| The first one creates an _implied bound_ , while the second is
| an explicit bound. The important thing about implied bounds is
| that they allow _late binding_ of lifetimes to function
| pointers (or closures): that is, you can have a single function
| pointer type like "for<'a> fn(&'a str)" that can be called
| with any lifetime 'a. Here, the lifetime 'a is called "late-
| bound", since it can be different with each call.
|
| In contrast, if a lifetime is subject to an explicit where
| bound, it must be "early-bound": each function pointer can must
| choose one particular value for that lifetime, that must be
| upheld for every call. For a practical example, you might have
| tried to declare a closure with a &T parameter, only to get
| lifetime errors when you call it twice. This is because the
| lifetime in the closure type is early-bound and must be the
| same for every call. (Sometimes the compiler figures out that a
| late-bound lifetime is desired, but the rules are very subtle.
| This is also why it's difficult to write a function that
| accepts a generic async closure.)
|
| Late binding is necessary for certain kinds of _variance_ ,
| which is a big part of this issue. Function pointers are
| contravariant in their parameters, which means if you have a
| type like "fn(&'short str)", you can cast it to "fn(&'static
| str)", since a 'static reference will always be valid for
| 'short. They're also covariant in their return type, so that
| "fn() -> &'static str" can be cast into "fn() -> &'short str".
| But when performing these variance transformations with late-
| bound lifetimes, the compiler doesn't always take into account
| the implied bounds in the source type properly, which allows
| you to perform casts that aren't actually sound.
| WirelessGigabit wrote:
| Edit (since I cannot edit my comment after 2 hours): for the
| code to properly verify the lifetimes one needs to write
| where 'b: 'a
|
| meaning 'b is at least as useful as 'a. So 'b can live the same
| time, or longer, but not less.
| robinsonrc wrote:
| It's good to see that at least Miri catches the issues. Hopefully
| the compiler can catch up with that!
| vlovich123 wrote:
| Does polonius catch the issue?
| throwawaymaths wrote:
| If you need a compiler sidecar anyways, why not just have a
| simpler language where all the borrow checking is done in a
| sidecar?
| dralley wrote:
| You don't "need" a compiler sidecar unless you're
| specifically writing code to exploit obscure bugs. This is a
| 0.0001% case
| throwawaymaths wrote:
| yeah or if you're using someone else's code that you're
| trusting doesn't have "obscure bugs" put there to smuggle
| in a vuln.
| dralley wrote:
| Nonetheless, it's far less of an issue than in any other
| language. You don't need 20 lines of crazy code to slip a
| serious security vulnerability into C, you can add one
| with a single character changed half of the time.
| pcwalton wrote:
| This is exaggerated re. C, but it is true that the
| important thing for practical security is whether memory
| safety is violated in practice, and obscure compiler bugs
| you have to go out of your way to try to exploit don't
| affect that. _That being said_ , this bug is pretty bad,
| and it should be fixed.
|
| I should point out that the reason the bug is difficult
| seems to mainly be because of backwards compatibility
| concerns. If compatibility weren't an issue, Rust could
| just ban function contravariance and nobody would care.
| muldvarp wrote:
| If you run somebody else's code and you don't trust that
| person, you need to read their code carefully anyway.
| Safe Rust code can do all kinds of shenanigans including:
|
| * Wiping your disk
|
| * Downloading and running code from the internet
|
| * ...
|
| Rust's safety guarantees don't exist to protect you from
| a malicious Rust programmer. They exist to protect you
| from mistakes a well meaning but fallible Rust programmer
| can make.
| throwawaymaths wrote:
| > Rust's safety guarantees don't exist to protect you
| from a malicious Rust programmer.
|
| This greatly ignores human behavior. A LOT of people who
| are reviewing rust code are going to only concentrate on
| unsafe blocks (that's if you're lucky), because rust is
| advertised as "rust gives you safety", and the word
| "safety" strongly suggests "don't worry about these
| things, they are safe". if the advertised safety is a
| false sense of safety that's a problem.
| muldvarp wrote:
| Do you have any data backing that argument? I could just
| as well make the argument that Rust has an extensive
| "correctness above all else" culture that encourages
| people to thoroughly review pull requests. Without data
| both of these claims are just that: claims.
| knose wrote:
| these people you're talking about would make the same
| mistake with code written in any language.
| steveklabnik wrote:
| Miri is fantastic, but also isn't a "compiler sidecar,"
| exactly. It is an interpreter. It's closer to a sanitizer
| than a compile-time check. It also means a huge, huge
| overhead to running your code, and in a performance-sensitive
| language that's a tough sell.
|
| It's also only needed for when you dip into unsafe, which is
| not particularly often, and so imposing this on all code
| would be the wrong tradeoff.
| vlovich123 wrote:
| Well libstd has unsafe all over the place. And as OP points
| out it catches this unsoundness issue in the language even
| though there's no unsafe anywhere, so it goes beyond just
| "dip into unsafe".
| steveklabnik wrote:
| Sure, but the unsafe in libstd is some of the most tested
| unsafe in existence. And sure, there are some soundness
| bugs, but in my experience they are edge cases that
| rarely come up. I don't think I have personally ever run
| into one in the wild.
|
| Regardless, it is not feasible for you to run your code
| under miri all the time, so you have to pick and choose
| where you use it, as a practical matter.
| vlovich123 wrote:
| Yup, just highlighting that it goes beyond more than just
| runtime checking for safeness of unsafe code only :)
| dralley wrote:
| Seems to rely on at least one known compiler bug (admittedly one
| open since 2015) https://github.com/rust-lang/rust/issues/25860
| dgfitz wrote:
| Looking at the syntax in that bug report makes me think I could
| never learn rust.
| faitswulff wrote:
| You could also think of it as "look at all the weird syntax
| olympics you need to do in order to trigger a compiler edge
| case!"
| the_mitsuhiko wrote:
| That is not syntax you would ever see.
| wredue wrote:
| I wrote pretty basic rust stuff and had to use lifetime
| syntax quite a bit. If you are just forcing deep copies all
| the time, you might not see it often, but if you're just
| going to deep copy everything always, you're probably going
| to be better off with another language to be honest as
| you're tossing away much of the benefit of using a language
| like rust anyway.
| pkolaczk wrote:
| I rarely use lifetimes and I don't deeply copy
| everything. This tool is probably the fastest in its
| class - does this code look like having a lot of
| lifetimes, cryptic syntax or deep copying?
|
| - https://github.com/pkolaczk/latte/blob/main/src/main.rs
|
| - https://github.com/pkolaczk/latte/blob/main/src/exec.rs
|
| There was one fundamental "aha" moment for me when it
| clicked: move semantics. Once I learned it, suddenly 99%
| of stuff became simple (including making async quite nice
| really, contrary to popular HN beliefs).
| wredue wrote:
| You can cherry pick all you want, although I would
| generally agree that "top level" executables will most
| likely bump against lifetimes far less frequently than
| libraries, frameworks, modules etc.
|
| I would also generally agree that a single pass through
| the official book should mostly be enough to be able to
| read and understand the op.
| pkolaczk wrote:
| Therefore I cited the core of the program as well. It
| also does a bunch of non trivial async processing there.
|
| I've written at least two other non-toy Rust programs and
| I haven't hit "lifetimes everywhere" problem there
| either. I guess the most lifetime related problems stem
| from trying to program the Java/Python/JS style in Rust -
| from overusing references and indirection. If you model
| your program as a graph of objects referencing each other
| then I can imagine you have a hard time in Rust.
| datadeft wrote:
| I use a lot of deep copy in my every day Rust and the
| performance is still 10x of my Python that I started to
| use in 2009.
| mattigames wrote:
| That's like saying you could never learn JS beacuse the
| following valid JS snippet, when in real life you would never
| see such thing unless someone its trying to obfuscate the
| code: [,{a,'z':q|0}[`${a?.x??b}`]]
| FpUser wrote:
| They've just written terse code with lots of single character
| identifiers. Not the style on would / should use for real
| life development of readable code.
| secondcoming wrote:
| Once you conquer the headaches and eye-strain, it's not too
| bad.
| wredue wrote:
| One of my criticisms of Rust is that it's a very RSI causing
| language. Very heavy on reaching awkward keys.
|
| I mean, a read through the guide makes this very readable,
| but it doesn't change that it looks like symbol soup and have
| lots of awkward repetitive strain to type it out.
| tuetuopay wrote:
| This is one I surprisingly very few complaints about the
| symbols used. My personal pet peeve are languages that use
| the walrus operator (:=) for variable
| assignment/declaration. Those two are on the same side of
| the keyboard, in the same finger column, so really slow and
| painful to type. If they were two keys for two different
| fingers, or better yet, for two different hands, that would
| be awesome (I have the same issue at work for the warsaw
| region abbreviated to waw). I write Rust daily and have yet
| to find such an inconvenience.
| CSSer wrote:
| Odd. I'll give you that it's slow to type, but I can't
| agree that it's painful. Colon is on the home row under
| my pinky, and equal is next to the delete/backspace key
| that I hit all day long.
| alpaca128 wrote:
| Depends on the keyboard layout you're using. Most languages
| are more convenient on an English layout than certain
| others because the syntax was most likely designed by
| someone using a qwerty keyboard. The symbols /[]'\;= are
| all accessible without modifier key.
|
| I have a custom layout on my keyboards which makes each
| symbol reachable without moving my hands, which made it a
| lot more pleasant.
| oneshtein wrote:
| `&` is a reference.
|
| `'a` is a label for a memory area, like goto labels in C but
| for data. You can read it as (memory) pool A. Roughly, when
| function entered, memory pool is created, then destroyed at
| exit.
|
| `'static` is special label for static data (embedded
| constants).
|
| `()` is nothing, like void in C.
|
| `&()` is an reference to nothing (an address) like `void
| const *` in C.
|
| `&&()` is an reference to reference to nothing, like `void
| const * const *` in C.
|
| `& 'static & 'static ()` is like void `const * const *` to a
| built-in data in C.
|
| `& 'a & 'b ()` tells compiler that second reference is stored
| in pool 'a, while data is stored in pool 'b. (First reference
| is in scope of current function.)
|
| `_` is a special prefix for variables to instruct compiler to
| ignore warning about unused variable. Just `_` is a valid
| variable name too. static UNIT: &'static
| &'static () = &&(); fn foo<'a, 'b, T>(_: &'a &'b
| (), v: &'b T) -> &'a T { v }
|
| Let's say that `&'a` is from a function `a()`, while `&'b` is
| from a function `b()`, which called from the function `a()`.
|
| The trick here is that we relabel reference from pool `'b`,
| from an inner function, to pool `'a` from outer function, so
| when program will exit from function `b()`, compiler will
| destroy memory pool `'b`, but will keep reference to data
| inside until end of the function `a()`.
|
| This should not be allowed.
| brink wrote:
| I think that's the best description of a lifetime I've seen
| so far.
| dgfitz wrote:
| Thank you very much for spending time explaining this. It
| makes more sense with that kind of description. I'm still
| not sure how long it would take someone like myself to be
| comfortable and proficient with the syntax.
|
| I guess stated another way, I don't generally have issues
| reading code from a wide swath of languages. If someone
| plopped me in front of a rust codebase I'd be at the mercy
| of the manual for quite a long time.
|
| Thank you again, sincerely.
| steveklabnik wrote:
| Rust decided to spend very little budget on syntax. The
| issue though, is that where it took said syntax from is
| very wide.
|
| & is used for references in a lot of languages.
|
| () is a tuple, same syntax as Python
|
| 'a isn't even novel: it was taken from OCaml, which uses
| it for generic types. Lifetimes are generic types in
| Rust, so even though it's not for an _identical_ thing,
| it 's related enough to be similar.
|
| _ to ignore a name for something has a long tradition in
| programming, sometimes purely as a style thing (like
| _identifier or __identifier), but sometimes supported by
| the language.
|
| fn name(args) -> {} as function syntax is not SUPER
| unusual. The overall shape is normal, though the choice
| between "fn," "fun," "func," or "function" varies between
| languages. The -> comes from languages like Haskell.
|
| <> for generics is hotly debated, but certainly common
| among a variety of langauges.
|
| the "static name: type = value;" (with let instead of
| static too) syntax is becoming increasingly normalized,
| thanks to how it plays with type inference, but has a
| long history before Rust.
|
| So this leads to a very interesting thing, where like,
| it's not so much that Rust's syntax is entirely alien in
| the context of programming language syntax, but can feel
| that way unless you've used a lot of things in various
| places. And that also doesn't necessarily mean that just
| because it's influenced from many places that this means
| it is coherent. I think it does pretty good, but also,
| I'd refine some things if I were making a new language.
| styfle wrote:
| > I'd refine some things if I were making a new language.
|
| Which parts would you change?
| steveklabnik wrote:
| About a year ago I wrote this:
| https://steveklabnik.com/writing/too-many-words-about-
| rusts-...
|
| I do think there's some good criticism of doing this,
| though, and so even a year later it's not clear to me
| it's a pure win.
|
| I am sympathetic to the vague calls to action by Aria et
| al. to improve the syntax for various unsafe features. I
| understand why we ended up where we ended up but I think
| overall it was a mistake. I am not sure I agree with her
| specific proposals but I do agree with the general thrust
| of "this was the wrong place to provide syntactic salt."
| (my words, not hers, to be clear)
|
| Ideally `as` wouldn't be a thing. Same deal, this is just
| one of those things that's the way it is due to history,
| but if we're ignoring all that, would be better to just
| not have it.
|
| I am still undecided in the : vs = debate for structs.
|
| I am sad that anonymous lifetimes in structs died six
| years ago, I think that would be a massive help.
|
| Probably tons of other small things. Given the context
| and history, I think the Rust Project did a great job.
| All of this is very minor.
| lmm wrote:
| > About a year ago I wrote this:
| https://steveklabnik.com/writing/too-many-words-about-
| rusts-...
|
| The = on functions idea is particularly interesting by
| comparison with Scala, which has shifted towards
| requiring = over time.
| jcranmer wrote:
| To be fair, even for someone who is comfortable in Rust,
| the example is definitely on the rather confusing side of
| the language. (Not entirely surprising--it's a bug about
| a soundness hole in the language, which invariably tends
| to rely on overuse of less commonly used features in the
| language).
|
| In particular, explicit use of lifetimes tends to be seen
| as somewhat more of a last resort, although the language
| requires it more frequently than I like. Furthermore, the
| soupiest of the syntax requires the use of multiple
| layers of indirection for references, which itself tends
| to be a bit of a code smell (just like how in C/C++, T *
| tends to be somewhat rare).
| mkehrt wrote:
| I write rust professionally and really like it, but I do
| think that its noisy syntax is a flaw. I'm not sure how I
| would clean this up, but it really can be inscrutable
| sometimes.
| datadeft wrote:
| For an anecdota it took me roughly a year. I was trying
| it several times. I still use .clone() a lot but it is
| getting better. :)
|
| The real question what is the use of Rust for you. Do you
| work on anything where Rust could be a value?
| viraptor wrote:
| > If someone plopped me in front of a rust codebase I'd
| be at the mercy of the manual for quite a long time.
|
| This is not a representative sample of Rust. That's
| explicitly triggering edge cases which requires abuse of
| syntax you wouldn't normally see.
|
| Check out this for something more realistic that anyone
| should understand https://github.com/ratatui-
| org/ratatui/blob/main/examples/ca...
| dingi wrote:
| If memory safety needs this level of fuckery to get going,
| it's not worth it for vast majority of software. Maybe
| stick to a GCed language until we can come up with a sane
| way to do it. These examples look cryptic as hell.
| steveklabnik wrote:
| It's cryptic because it is a reduced test case to show
| off a bug. 99.9% of code doesn't look like this.
|
| Just because "int ( _(_ foo)(void ))[3]" is a valid C
| declaration doesn't mean that all of C code looks like
| that.
| pcwalton wrote:
| This is basically the Rust equivalent of the obfuscated C
| code contest and indicates little to nothing about the
| actual Rust people write.
| datadeft wrote:
| > this level of fuckery to get going
|
| C++ has the same level of fuckery without memory safety.
| I don't think there is an extreme level of fuckery in
| Rust. I wish they got more influence from the ML
| languages than C++ but it is not unbearable.
| dralley wrote:
| You should treat this the same as you would something like
| the Obfuscated C competition. Just because it's supposed to
| compile and run properly doesn't mean it's good code.
| estebank wrote:
| You're right, but in this case it _shouldn 't_ compile :)
| GaggiX wrote:
| I thought it was just using the totally safe transmute, this
| one:
| https://blog.yossarian.net/2021/03/16/totally_safe_transmute...
|
| But I guess there is another trick.
| Georgelemental wrote:
| As noted on that GitHub page, fixing the bug will become
| possible once rustc adopts a new trait solver:
| https://blog.rust-lang.org/inside-rust/2023/07/17/trait-syst...
| Maken wrote:
| Everything is always a new solver away.
| TylerE wrote:
| a _sufficiently advanced_ new solver. Just like how the
| lisp guys insisted for decades that a sufficiently advanced
| compiler would solve all their performance woes. Right up
| until it became obvious that it couldn 't.
|
| Feels a bit like early chess engines. They tried to create
| super sophisticated heuristics to determine move quality,
| but then one person (or more probably multiple people
| independently more or less simultaneously) realized it's
| both easier and better to just do more or less the simplest
| thing that can possibly work, but do it as much as possible
| in the allotted time.
|
| In other words - don't try to logically decide on the best
| move with some super advanced set of rules. Just assign a
| fairly basic and straightforward score based on calculating
| every reasonble move out 15, 20, 30 moves deep.
| Georgelemental wrote:
| In the case of the trait solver, there is a clear end
| goal to reach: "the type system should be sound." This is
| a yes/no question, which you can prove/disprove
| mathematically.
|
| (I suppose chess engines also have an end goal of perfect
| play, but that is a much harder problem)
| dzaima wrote:
| There's a very simple solver that completes that goal:
| one that reports that it cannot prove any code as
| correct. So there must be a secondary goal of "maximize
| the amount of accepted code", which is a significantly
| less trivial question.
| TylerE wrote:
| Yes, exactly.
|
| Saying "just do the thing correctly, duh!" is easy. Ya
| know the saying about the difference between theory and
| practice?
| meindnoch wrote:
| Is it going to be even slower than the current one?
| dralley wrote:
| Who says the current one is slow?
|
| Just because the compiler as a whole isn't the fastest one
| around doesn't mean the responsibility falls equally on
| every constituant part of the compiler.
| estebank wrote:
| The most obvious slow down people perceive of compile
| times is due to monomorphization. It is a combinational
| explosion when instantiating every generic type that is
| in use, _and_ it exacerbates the issue of rustc producing
| verbose LLVM IR bytecode.
| nialv7 wrote:
| Can you quote the relevant part? Because IIUC lifetime checks
| have nothing to do with the trait solver.
|
| There's a new borrow checker however, but that's not going to
| fix this either
| nicklecompte wrote:
| My biggest concern with Rust is the sloppiness around the
| distinction between a "compiler bug" and a "hole in the type
| system." This is more of a _specification bug_ even though Rust
| doesn 't have a formal specification: the problem is the design
| of the language itself, not that a Rust programmer wrote the
| wrong thing in the source code:
| https://counterexamples.org/nearly-universal.html?highlight=...
| ggreg84 wrote:
| > My biggest concern with Rust is the sloppiness around the
| distinction between a "compiler bug" and a "hole in the type
| system."
|
| That bug is marked as I-unsound, which means that it
| introduces a hole in the type system.
|
| And so are all other similar bugs, i.e., your concern seems
| to be unfounded, since you can actually click on the
| I-unsound label, and view all current bugs of this kind (and
| past closed ones as well!).
| nicklecompte wrote:
| Perhaps I should have said "hole in the type theory" to
| clarify what I meant.
|
| To be clear I wasn't trying to imply the rustc maintainers
| were ignorant of the difference. I meant that Rust
| programmers seem to treat fundamental design flaws in the
| language as if they are temporary bugs in the compiler.
| (e.g. the comment I was responding to) There's a big
| difference between "this buggy program should not have
| compiled but somehow rustc missed it" and "this buggy
| program will compile because the Rust language has a design
| flaw."
| pcwalton wrote:
| But it's not a fundamental design flaw in the language,
| nor is it a "hole in the type theory". It's a compiler
| bug. The compiler isn't checking function contravariance
| properly. Miri catches the problem, while rustc doesn't.
| nicklecompte wrote:
| I believe it really is a flaw in the language, it's
| impossible for _any_ compiler to check contravariance
| properly in this edge case. I don 't think anything in
| this link is incorrect:
| https://counterexamples.org/nearly-
| universal.html?highlight=... (and it seems the rustc
| types team endorses this analysis)
|
| I am not at all familiar with Miri. Does Miri consider a
| slightly different dialect of Rust where implicit
| constraints like this become explicit but inferred? Sort
| of like this proposal from the GH issue:
| https://github.com/rust-
| lang/rust/issues/25860#issuecomment-... but the "where"
| clause is inferred at compile time. If so I wouldn't call
| that a "fix" so much as a partial mitigation, useful for
| static analysis but not actually a solution to the
| problem in rustc. I believe that inference problem is
| undecidable in general and that rustc would need to do
| something else.
| anderskaseorg wrote:
| In the language, fn foo<'a, 'b, T>(_:
| &'a &'b (), v: &'b T) -> &'a T { v }
|
| should be equivalent to fn foo<'a, 'b,
| T>(_: &'a &'b (), v: &'b T) -> &'a T where 'b: 'a { v }
|
| because the type &'a &'b is only well-formed if 'b: 'a.
| However, in the implementation, only the first form where
| the constraint is left implicit is subject to the bug:
| the implicit constraint is incorrectly lost when 'static
| is substituted for 'b. This is clearly an implementation
| bug, not a language bug (insofar as there is a
| distinction at all--ideally there would be a written
| formal specification that we could point to, but I don't
| think there's any disagreement in principle about what it
| should say about this issue).
| comex wrote:
| You mean that Miri catches it at runtime, right? If so,
| that hardly demonstrates anything about the difficulty or
| lack thereof of fixing rustc's type checker, since Miri
| is not a type checker and doesn't know anything about
| "lifetimes" in the usual sense.
|
| I agree that this isn't a "fundamental design flaw in the
| language", but Miri is irrelevant to proving that.
| nialv7 wrote:
| Why is this distinction important? It's still something
| you fix by changing what programs the compiler accepts or
| rejects. Or were you trying to imply this is unfixable?
| chatmasta wrote:
| The distinction matters because any existing code that
| breaks with the compiler fix was either relying on
| "undefined behavior" (in the case of a compiler bug
| incorrectly implementing the spec), so you can blame the
| user, or it was relying on "defined behavior" (in the
| case of a compiler bug correctly implementing a badly
| designed spec), so you can't blame the user.
|
| I suppose the end result is the same, but it might impact
| any justification around whether the fix should be a
| minor security patch or a major version bump and breaking
| update.
| estebank wrote:
| Rust's backwards compatibility assurances explicitly
| mention that breaking changes to fix unsoundness are
| allowed. In practice the project would be careful to
| avoid breaking more than strictly necessary for a fix.
|
| In the case of user code that isn't unsound but breaks
| with the changes to the compiler/language, that would be
| breaking backwards compatibility, in which case there
| might be a need to relegate the change to a new edition.
| nialv7 wrote:
| Well first of all, Rust doesn't even have a spec. And I
| would also advocate for not blaming anyone, let's just
| fix this bug ;)
| nicklecompte wrote:
| Probably better to be maximally pedantic here:
|
| - Assume our language has a specification, even if it's
| entirely in your head
|
| - a "correct" program is a program that is 100%
| conformant to the specification
|
| - an "incorrect" program is a program which violates the
| specification in some way
|
| Let's say we have a compiler that compiles _correct_
| programs with 100% accuracy, but occasionally compiles
| _incorrect_ programs instead of erroring out. If the
| language specification is fine but the compiler
| implementation has a bug, then fixing the compiler does
| not affect the compilation behavior of _correct_
| programs. (Unless of course you introduce a new
| implementation-level bug.) But if the language
| specification has a bug, then this does not hold: the
| specification has to change to fix the bug, and it is
| likely that at least some formerly correct programs would
| no longer obey this new specification.
|
| So this is true:
|
| > It's still something you fix by changing what programs
| the compiler accepts or rejects
|
| But in one case you are only changing what _incorrect_
| programs the compiler accepts, and in the other you are
| also changing the _correct_ programs the compiler
| accepts. It 's much more serious.
| lmm wrote:
| > Or were you trying to imply this is unfixable?
|
| If it's been known since 2015 and not fixed, that's
| pretty suggestive.
| layer8 wrote:
| It's a consequence of not having a formal and formally-
| proved type system.
| Karellen wrote:
| > GLWTS(Good Luck With That Shit) Public License
|
| > The author has absolutely no fucking clue what the code in this
| project does. It might just fucking work or not, there is no
| third option.
|
| lol
| omoikane wrote:
| Sounds like an updated version of WTFPL
|
| https://en.wikipedia.org/wiki/WTFPL
| infamouscow wrote:
| How is an alternative Rust(tm) implementation supposed to
| approach failures like this. Especially when the organization
| that controls the name and branding has a death grip on things.
| It's very easy for the foundation to just get their lawyers to
| send you a cease and desist in the US.
|
| As someone that relentlessly mocks hifalutin software engineering
| in favor of practical solutions for humans, it seems highly
| impractical, if not outright impossible for fully-fledged
| alternative compiler to flourish in this ecosystem.
|
| This is very stultifying. I can see why people ignored this
| nonsense from the start, or moved to other languages.
| steveklabnik wrote:
| I don't know what you mean. This is a compiler bug? Nobody
| claims that other compilers must also have the exact same bugs.
| adastra22 wrote:
| It is a compiler bug, yes.
| steveklabnik wrote:
| Sorry, I was typing the way I talk again, this doesn't
| always come across well in text. Yes, thanks for making
| sure that's known.
| nicklecompte wrote:
| It's really a bug in the design of Rust itself, not in the
| implementation of rustc: https://counterexamples.org/nearly-
| universal.html?highlight=... The fix needs to happen at the
| level of type-theoretic computer science, not clever Rust
| programming. And in particular, an alternate implementation
| of Rust which fixes this bug _would have a different type
| system_ than rustc, and arguably should be considered a
| separate dialect of Rust (at least unless rustc is updated
| accordingly).
| steveklabnik wrote:
| I think in the same way that you are frustrated because
| people say "it's a compiler bug" too often, this feels like
| going in the other direction too much. The higher order
| bit, to me, is the "segfault in safe code means a bug"
| stance that the overall project takes. Soundness holes
| happen. Sometimes they take a while to transition away
| from. Arguing that the compiler faithfully implements an
| algorithm that causes bugs still means, from a higher
| perspective, that the compiler has a bug: they have chosen
| an inappropriate algorithm here.
|
| There is no specification of the specific implementation
| here. Rust could in fact (at least in theory, I am not an
| expert on this specific problem) transition to a different
| path here where the bug does not manifest. RFC 1214 is an
| example of the Rust Project successfully doing this, and
| hilariously enough, that situation is one of the things
| that led to this one!
| nicklecompte wrote:
| No, the issue is NOT that they chose "an inappropriate
| algorithm," they chose an inappropriate _design of the
| language itself._ From the link [some typos might be mine
| since I manually retyped some of the TeX symbols}:
|
| > With constrained types, there are two different ways to
| interpret a polymorphic type such as \forall a. C[a] ->
| String:
|
| > 1. "\forall a" really means "for all a" so the type
| above is invalid since C[a] is not a valid type for all
| a.
|
| > 2. "\forall a" really means "for all a for which the
| right hand side is valid," so the type above is valid,
| but it can only be used for a = Int.
|
| > ...
|
| > Option (2) requires slightly less annotation by the
| programmer, but it can be easy to lose track of the
| implicit constraints on a. Rust chooses this option,
| which led to a tricky bug.
|
| And if you go on to read what this tricky bug is, it does
| not involve any _incorrect_ implementations or even any
| specific algorithm. It 's conflicting _requirements_ :
| the design of co/contravariance of lifetimes in Rust
| conflicts with the design of nearly-universal
| quantification over types: one or the other has to be
| changed in its core semantics, not merely patched with a
| better algorithm.
|
| ETA: My point is that an alternative compiler cannot
| simultaneously solve this "compiler bug" and be
| appropriately source-compatible with rustc. It seem like
| they would either have a different syntax for type
| quantification, or more stringent constraints on
| lifetimes.
| steveklabnik wrote:
| It's clear we're not going to see eye to eye here.
| "conflicting requirements" is a bug. It should be fixed.
| It may be hard to fix. It may take a long time to fix.
| That may mean "redesigning." Still a bug.
| nicklecompte wrote:
| I think we're not seeing "eye to eye" because you're not
| actually responding to my comment! You seem to be
| responding to something else. I said "it's really a bug
| in the design of Rust itself" and you agree.
|
| My point is that you said
|
| > Nobody claims that other compilers must also have the
| exact same bugs.
|
| And that's true! But it's not relevant. Most people say
| other compilers should have
|
| 1) the same underlying type theory as rustc
|
| 2) almost complete source-compatibility with rustc
|
| If, say, "rustd" satisfies 1) and 2), then it's
| guaranteed to have many of the same memory
| vulnerabilities as rustc. The only way to fix these
| vulnerabilities is to break 1) or 2), which is an awfully
| steep price.
| vlovich123 wrote:
| FWIW C and C++ also have plenty of compiler-specific and
| language errata due to unsoundness issues in the spec /
| implementation bugs following the spec. I don't really
| follow how an alternate Rust compiler would solve this
| since a language errata needs to be fixed at the language
| level & would impact all conforming compilers unless this
| alternate Rust compiler basically forked the language, in
| which case the Rust trademark is good as it lets people
| disambiguate between the forks.
|
| If it's a design issue, the Rust team is doing pretty
| good on that front I think in terms of exploring new ways
| of represting the type system (e.g. Polonius & the Trait
| solver work that's moving forward).
|
| Also, even if it somehow a new compiler somehow improved
| soundness without forking the language, especially for a
| language as complex as Rust, at best you're just picking
| an alternate set of bugs. AFAICT the Rust team follows
| best practices in terms of developing the code & managing
| the people working on it, so it's hard to imagine an
| alternate compiler team would outperform their progress
| until the Rust project itself stagnates. Also, any
| successful fork would likely start with rustc as the
| basis anyway (the struggles and slow-going GCC is having
| reimplementing Rust within GCC should exemplify why a
| separate frontend isn't helpful).
| woodruffw wrote:
| I think they'd treat it like any other compiler bug. LLVM is
| not bug-for-bug compatible with GCC, and doesn't attempt to be.
| adastra22 wrote:
| What's your shell prompt config that you screenshotted in the
| README?
| xal wrote:
| looks like https://starship.rs/
| hpb42 wrote:
| Oh, cool. They implemented a `download_more_ram()` function![0]
|
| Does it crash safely as well? I did not test it, I more than 640
| KB of ram.
|
| - [0] https://github.com/Speykious/cve-
| rs/blob/d51f52dd64f148a086e...
| foolswisdom wrote:
| What a wonderful license! https://github.com/Speykious/cve-
| rs/blob/main/LICENSE
| poly_morphis wrote:
| I could get behind that type of short, clear legalese.
| yjftsjthsd-h wrote:
| > 0. You just DO WHATEVER THE FUCK YOU WANT TO as long as you
| NEVER LEAVE A FUCKING TRACE TO TRACK THE AUTHOR of the original
| product to blame for or held responsible.
|
| A thing of beauty^_^ I've seen a lot of licenses that require
| attribution[0], so a license that explicitly demands that you
| _not_ credit the author is a lovely twist:)
|
| [0] Read: Nearly all of them; even permissive BSD-like licenses
| usually(?) require that much.
| saagarjha wrote:
| Are you the safest because you're Rust? Or are you Rust because
| you are the safest?
| datadeft wrote:
| The real question is: what is the chance that somebody runs into
| this while implementing something common.
| vacuity wrote:
| It's not about frequency; it's about the possibility that just
| one of these unsoundness bugs gets through to production and
| screws things up. The whole point of memory safety languages is
| that, unless you (or a dependency author) dives into the dark
| arts of whatever language you're working in, you are
| _guaranteed_ to not encounter use-after-free or null-pointer-
| dereference. Practically, maybe such a bug gets patched
| quickly, and no one exploits it as a vulnerability. In
| hindsight, then, it wasn 't so bad. But you don't want to be
| the one who is called at 2 AM because an impossible segfault
| happened and wrecked the database.
| fweimer wrote:
| Without compiler bugs, on stock Linux, you can achieve the same
| thing via /proc/self/mem. Rust makes this very easy because you
| can get the raw address from safe code, so no guessing is
| required which memory location to patch.
|
| For the compiler bug, as someone who never strayed into those
| regions of Rust programming, it's not clear to me how likely it
| is that someone would write such code by accident and introduce a
| memory safety issue into their program. I browsed the Github
| issues, but couldn't find an indicator how people encountered
| this in the first place.
| jcmoyer wrote:
| >Without compiler bugs, on stock Linux, you can achieve the
| same thing via /proc/self/mem.
|
| The documentation addresses this case specifically:
| https://doc.rust-lang.org/stable/std/os/unix/io/index.html#p...
|
| "Rust's safety guarantees only cover what the program itself
| can do, and not what entities outside the program can do to it.
| /proc/self/mem is considered to be such an external entity..."
___________________________________________________________________
(page generated 2024-02-20 23:01 UTC)