Threads for Ada_Weird

    1. 12

      The outcome is worse software for everyone: less secure, more buggy, with fewer features and worse performance.

      Citation needed; this is very rare in my experience. The majority of updates to libraries involve changes that are not relevant to the application updating them, or if they are relevant, they are as likely to introduce new bugs as to fix existing ones.

      1. 12

        In Rust? Regressions absolutely happen (I’ve dealt with three in the last year) but overall Rust dependencies tend to be quite solid.

        The risk of not updating is really high in cases where a security bug is found and you’re wildly out of date. In an emergency you have to either backport the patch yourself (not being an expert on the dependency), hope the maintainers backport the patch for you (or pay them large sums of money), or update to the latest version (introducing a ton of risk at a time where risk should be minimized).

        Staying abreast amortizes this risk over non-emergency periods of time.

        1. 19

          Staying abreast amortizes this risk over non-emergency periods of time.

          This is the most important thing: if you only update when a CVE is published, then you’re doing so under duress, in a hurry, and all at once. It’s much harder to update past several versions of changes because we woke up to an active security threat than to update dependencies on a schedule with smaller piece meal changes needed each time.

          1. 2

            People keep saying this, but I have only ever encountered it once in the two decades I’ve been writing software. And when it did happen, it was for a subsystem we were about to delete, so if we had taken this advice we would have wasted a bunch of time updating code that never even would have used the fixed update.

            Maybe it’s just a thing that happens in frontend work?

            1. 6

              This happens all the time across several tech stacks in companies I’ve worked in. I mean even if we just look at famous cases from the last few years there’s been issues with log4j, xz, openssl. It feels if you’re not encountering CVEs in dependencies, you’re just not looking.

              Now granted, not all CVEs are real issues. The security department when I worked at Yahoo loved to insist on urgent updates of Jackson because their security scanner said it had a RCE. The RCE being if you turned on the feature to let the client choose the class to instantiate (off by default, we didn’t enable it, it’s highly warned about in the docs), and loaded one of the classes in the JVM ecosystem that executed shell commands from the constructor, then the client could ask the server to construct that object and execute code. The response of the Jackson project is generally to add such classes to their blacklist for that feature when they’re reported, and to continue to warn people not to enable that feature. Which shut the CVE scanners up for a few months, until someone discovered the next library with a ShellCommand class or equivalent.

              1. 2

                It feels if you’re not encountering CVEs in dependencies, you’re just not looking.

                I didn’t say I don’t encounter CVEs.

                What I said was that the situation of “you’ll wish you had been upgrading all along because a CVE will come along suddenly where the upgrade that fixes it will take a lot of work, and you would have been better off if you had already done most of that work by doing the upgrades in between” is not something I’ve ever encountered outside code that I was going to delete anyway.

                (And yes, of course most CVEs are actually “Curriculum Vitae Enhancers” and not legitimate problems.)

                1. 3

                  I’m glad you’ve been so lucky! I have been in situations where it’s been necessary to update across many versions to fix a security bug. It’s terrifying, basically requiring one person to manually review everything in between whole another person prepares the patch.

          2. 5

            As the source of one of those regressions: adding cargo-server-checks to my GitHub workflow gives me peace of mind, especially since most of my crates only see a small spike of activity in a year which makes mistakes much more likely.

          3. 4

            Agreed. I came to Rust because Rust is so much better at “fearless upgrades” than any other language I’ve ever used and I’m not sure I’ve ever had a semver-compatible GitHub Dependabot bump fail the resulting CI test run without it being my cargo deny task complaining that a dependency has added a new license via transitive dependency which I need to audit and whitelist.

            1. 16

              Three regressions I’ve dealt with recently are:

              • mio/Tokio on illumos stopped working (I work at Oxide where we use illumos)
              • a pretty bad memory corruption issue in libc
              • a behavior change in config-rs

              But these are rare, and the fact that they’re rare means that when they happen, it’s ok to prioritize dealing with them.

              1. 4

                The time inference regression comes to mind, but that was the stdlib, not the ecosystem.

              2. 1

                Just out of curiosity, but when did you come to Rust? In the first couple of years it seemed like every important/useful crate was 0.x and even I, with only a couple of toy projects, ran into lots of issues. (regex, date, etc).

                I would not be surprised if people had learned this lesson early. I’m not saying it’s a good thing.

                1. 5

                  It was sort of a gradual ramp-up. After discovering Rust via Planet Mozilla, I was lurking in /r/rust/ waiting for v1.0 and started learning the same day the syntax stabilized, but things like using Dependabot came later. (And I’m not usually the kind of person to try out a new language every year or two. Rust just filled a niche that I’d been looking for.)

                  Still, even then, Rust’s type system made it more “fearless upgrades” than Python.

                  1. 2

                    Unfortunately, 0.x doesn’t mean much in the Rust ecosystem.

                    There are of course unstable, broken, and toy crates with 0.x versions, but there also many 0.x crates that are stable and production-ready: libc, rand, log, futures, itertools, toml, hashbrown are all 0.x, but they’re serious packages, with millions of downloads, and they’re used even in rustc and Cargo themselves, so they are as reliable as Rust is.

                    1. 2

                      There’s absolutely still an issue with critical crates being at 0.x versions. Winit for example is up in the brain stem of a lot of GUI work in Rust and they updated the way their event loop worked to go from taking a closure that accepts an event and will dispatch it as the user sees fit to requiring you to implement a trait that has methods for different types of events, ostensibly for compatibility reasons although I’m unsure of the details.

                2. 9

                  One thing that Rust can’t optimize nicely is this pattern:

                  a[0] + a[1] + a[2] + a[3]
                  

                  because each read has its own bounds check, and each check reports the exact location of the failure, so they are all unique jumps that can’t be merged. You get 4 branches, 4× code bloat for error message setup, and miss out on autovectorization.

                  I don’t know how an optimizer could improve this. Usually it’s not safe to reorder code around safety checks. If there are any writes happening at the same time, the partially-written data may be observable and make it even semantically impossible to optimize out the redundant checks. Currently this needs coalescing the checks manually with code like:

                  let a = &a[..4];
                  
                  1. 7

                    Oh, that’s an interesting approach to doing the coalescing. I’d always just seen it done with assert!(a.len() >= 4). Looks like they generate basically identical code, which is nice~

                    I wonder if it could be improved by relaxing the “report the exact location of the failure” constraint? So it wouldn’t report “out of bounds access in a[3]”, but might report “out of bounds access in a[0] + a[1] + a[2] + a[3]” or such…

                    1. 5

                      I came here to post the assert!(a.len() >= 4) version, haha. That’s the way I’ve seen this done.

                    2. 3

                      I guess that release builds could have less informative error reporting so that the error paths can be coalesced?

                      1. 7

                        Even if I replace the panic with a direct call to abort() or even loop {}, LLVM still chooses to have four branches:

                        https://rust.godbolt.org/z/9ehfEbhao

                        1. 6

                          That looks weird. LLVM should be able to handle that. Might be worth extracting a test case of LLVM IR and looking at why it isn’t getting fixed. Just looking at the LLVM IR view in compiler explorer, I would expect LLVM to hoist and merge the checks easily.

                          1. 3

                            It seems like it’s assuming that the branches would be predicted false by the CPU so while it would cost it would cost relatively little. It’d be nice if it managed to figure out that it could choose to compare to 4 and if lt’s less than then use a lut to figure out what to pass to panic_bounds_check?

                            1. 3

                              How disappointing! I expected better than that. Thanks for making that example.

                          2. 3

                            The OCaml compiler intentionally generates a vague error message on array access that does not depend on the failing index, to make it easy to share all failure cases in a single non-returning instruction, to emit fast code. On the other hand, it does not try factoring the bound checks together, so we still have four checks where one would suffice. (But does it matter in practice, if those checks are accurately predicted?)

                            1. 8

                              does it matter in practice, if those checks are accurately predicted?

                              It may not matter when it’s reading big objects or through an indirection. However, in numeric code it matters quite a lot, because the primary cost is not in the branch itself, but in lost optimizations. The branches with side effects prevent loading data in larger chunks, and kill autovectorization and most other loop refactorings.

                              1. 2

                                It looks like gcc and clang still emit multiple jumps here? It’s been a while since I’ve done asm though so it’s entirely possible I’m misinterpreting this? https://godbolt.org/z/WxYd5KhMd

                            2. 2

                              What’s the difficulty here? Maybe I don’t understand how the bounds checking / error reporting works, but could you not coalesce them like this:

                              if (a.len < 4) {
                                // Do detailed error reporting here based on len and where it would fail
                              } else {
                                // Do unchecked sum here
                              }
                              
                              1. 6

                                I can, but I’d like the optimizer to be smart enough to do it automatically. I suspect it’s really hard to explain to the optimizer that the bound check is both a side effect so big it can never be ignored, but also so insignificant, that it can be reordered and coalesced.

                                1. 2

                                  Ok, it sounded a lot more like a fundamental thing in your first comment. This seems much more like an optimizer issue, maybe with branch hints (you make the error paths cold) and somehow hoist the checks into the cold path and the hot path has the coalesced checks only? That seems doable, even at the LLVM level. (This is assuming you want the same guarantees of exact error reporting).

                                  (I checked and get the same problem with Zig btw: https://godbolt.org/z/PW8sYPe5d)

                            3. 4

                              I’ve also mentally compiled a few ideas of a “nicer” C over the years . Aside from the ones in the article there’s also “generics”:

                              #define len(X) _Generic((X), \
                                  const char*: strlen, \
                                  mytype: mytypelen \
                               )(X)
                              

                              Another thing I really like is the middle dot separator that Go’s source code had in its infancy:

                              int string·len(string s)
                              void net·Listen(/* ... */)
                              

                              You can also stray even further from C and define a few macros that communicate intent but do nothing:

                              #define mut 
                              #define private static 
                              private void foo(mut int *n) {
                              	// mutates n
                              }
                              

                              There are some other ideas (like range macros or defer pre-processor) that are probably harmful in the long run in a project with more than 1 person in it. Apparently Bash’s original source code had a lot of these language modifications that made it unmaintainable. But with a few macros and strong conventions you get a nicer looking language.

                              1. 2

                                I like that middle dot idea. How would you do a defer pre-processor? Is that just a macro or something more (like the cleanup attribute extension)?

                                This is actually all quite relevant to an experiment I’ve been doing lately with a custom codegen step before C compilation. I used to not be into codegen due to the seeming inconvenience of an extra build step, but it’s been fine, I just put it in my build script and then don’t think about it now. The idea with the codegen is to read your types and only generate additional types and functions in additional files you #include, but not eg. transform the code inside function bodies or such. So far what I’ve gotten with it are (for each user type T):

                                • void TDealloc(T *value) functions that propagate dealloc calls to each field of a struct recursively, offering one of the features of destructors that I find useful. You still do the top-level call explicitly, which I like.
                                • TArray types for growable arrays that are type-safe. Also generates macros to allow for (TArrayEach(elem, array)) { ... }.
                                • TFromJSON and TToJSON functions for json serialization and deserialization.

                                The clear names give me some things I like over templates / generics / ast-macros / comptime / … – you can ‘go to definition’ of these functions in your editor and just read them, step into them in a debugger, have readable callstacks in debuggers and profilers, global symbol search with the unique name and find the function, etc. When the generated code has compile errors they take you to the concrete examples where they fail. The generated code is just regular, readable code.

                                I’ve been working on a little game project to test it out: https://gist.github.com/nikki93/0ffa13a3b6e690c0317065ba0b415433 – ‘game.c’ is meant to be the main user-written code. ‘generate.c’ reads that to generate ‘generated.{h,c}’. And ‘main.c’ is surrounding scaffolding to launch the game and also provide ‘hot-reload’ that uses the serialization.

                                Here’s a quick video showing the resulting workflow: https://youtu.be/zGelkFXP4mo – the hot reload, debug stepping into generated functions, going to definition.

                                I’ve had a Go->C++ transpiler (https://github.com/nikki93/gx) I’ve used a lot for this kind of thing before but I’m finding that this C + codegen is getting me most of what I wanted from that. I’m liking the ‘explicitly writing types everywhere’ vibe for comprehension after-the-fact.

                                1. 1

                                  How would you do a defer pre-processor?

                                  With codegen. The idea is to just parse the source code for defer and generate an output where the defer statement is put before every return in order.

                                  #define defer(X) (void)0
                                  void func() {
                                  	sometype *s = sometype·new();
                                  	defer(free(s));
                                  
                                  	if (true) {
                                  		return;
                                  	} else {
                                  		return;
                                  	}
                                  }
                                  

                                  Becomes:

                                  void func() {
                                  	sometype *s = sometype·new();
                                  
                                  	if (true) {
                                  		free(s);
                                  		return;
                                  	} else {
                                  		free(s);
                                  		return;
                                  	}
                                  }
                                  

                                  It has to be a little bit smarter because when the original if/else/whatever doesn’t have braces then it’d have to add it, etc.

                                  At some point too many of these modifications become more like a custom language that compiles to C, without any modern conveniences. However in general I agree with you that codegen can be clearer than generics/templates in some cases. It also shines in some other more basic scenarios where there would be a perfectly valid solution without codegen (e.g. Protobufs, OpenAPI, SQL query functions, etc).

                                  1. 3

                                    Oh gotcha. Yeah __attribute__((cleanup(...))) in clang and gcc might be a reasonable way to get at this if using those compilers. I like that that attribute associates a specific function with a specific variable (the usage of defer that is usually desired), rather than usual designs of defer which involve an arbitrary code block and writing the defer anywhere.

                                    I agree re: your point about a custom language. I’m thinking to limit my codegen to just only generate additional types and functions – essentially more regular API – for your code to use – rather than transform your code meaning to replace it. I think that sets a good boundary that keeps user code still feeling like regular C. I find that what I want more from C tends to be what APIs I’d like to be available on my types (reflection, generic growable array or other data structures), more than transformations of expressions and statements.

                                    This balance without the ‘modern conveniences’ is actually what I’m wanting right now because I find that those conveniences often lure me into being distracted by trying to make my code ‘nicer’ using language features, or involve me having to make decisions about which way to write something – more accidental decisions that aren’t actual design decisions about the project I’m working on. eg. I find deciding if a function should be a method or a free function to usually fall under this category in modern languages. By just removing the need to make such decisions I’ve found that C can keep me focused on actually just working on the project. It’s a delicate balance for sure, and the tradeoffs vary across projects and authors (and especially would break down in teams) – but very reasonable for the ‘fun single person project’ category.

                                2. 2

                                  The windows headers actually do something similar to the mut concept you put in with in, out, and inout parameters. They’re definitely a nice way to communicate intent in a function signature.

                                  1. 1

                                    There are some other ideas (like range macros or defer pre-processor) that are probably harmful in the long run in a project with more than 1 person in it. Apparently Bash’s original source code had a lot of these language modifications that made it unmaintainable. But with a few macros and strong conventions you get a nicer looking language.

                                    Were you thinking of the Unix V7 Bourne shell? Stephen Bourne used macros to make C look like ALGOL 68. Here’s an example file.

                                    1. 1

                                      You’re right, I mixed up the two: Bourne-Again Shell wasn’t written by Bourne. Rob Pike had a talk where he talked (briefly) a little more fondly about the Bourne shell and those macros.

                                  2. 3

                                    This is written from the perspective of a CLisper, would be interesting to see the article written from the perspective of a Schemer.

                                    1. 6

                                      Yeah this is, uh… pretty one-sided. I haven’t done a ton of Scheme, but I would expect such an article to at least cover:

                                      • booleans and the absence of nil in Scheme
                                      • continuations
                                      • macro hygiene (I don’t love Scheme’s approach to hygiene but it’s better than nothing at all)
                                      • immutable data
                                      1. 7

                                        Yeah.

                                        Many things in Common Lisp that are missing in Scheme … Some of these features include … a powerful macro feature for extending the language

                                        I stopped reading at that point!

                                        1. 3

                                          Is that referring to reader macros? If not, then I don’t understand.

                                          1. 6

                                            Hygenic macros weren’t part of the language until R5RS. Implementations each had their own bespoke way of doing things. That was a long time ago though.

                                            1. 3

                                              R5RS macros were an appendix in R4RS, which is an even longer time ago :-) Here’s a pretty good summary of the history of macros in Scheme

                                              1. 1

                                                Ah, interesting! I don’t think I’ve used any pre-R5RS Scheme… maybe R4RS, but not sure.

                                        2. 1

                                          This seems semi-intuitive to me? A move constructor in C++ constructs a new object while leaving the moved from object in a state where the old object can have its destructor called on it. Calling the copy constructor is a valid implementation of those requirements.

                                          1. 8

                                            Couple of observations. In the example, if all array elements are passed in registers, how do you expect to handle variable indexing? I.e.

                                            fn variable_extract_i64(idx: usize, arr: [i64; 3]) -> i64 {
                                                arr[idx]
                                            }
                                            

                                            My understanding is that implementing this pretty much requires writing arr to the stack and then reloading.

                                            The Go internal calling convention makes the same point (emphasis mine):

                                            Function calls pass arguments and results using a combination of the stack and machine registers. […]. However, any argument or result that contains a non-trivial array or does not fit entirely in the remaining available registers is passed on the stack. […]

                                            Non-trivial arrays are always passed on the stack because indexing into an array typically requires a computed offset, which generally isn’t possible with registers. Arrays in general are rare in function signatures (only 0.7% of functions in the Go 1.15 standard library and 0.2% in kubelet).

                                            And gives up on arrays of size bigger than 1:

                                            Register-assignment of a value V of underlying type T works as follows: […]

                                            • If T is an array type of length 0, do nothing.
                                            • If T is an array type of length 1, recursively register-assign its one element. ) - If T is an array type of length > 1, fail. […]

                                            Also, how often is generating an ad-hoc calling convention worth it, where the same compilation time could be spent on more aggressive inlining/LTO?

                                            1. 2

                                              Since it’s passed by value, it could just be spilled to the stack if/when you need it. Since it’s a fixed small size, typical iteration should be able to be unrolled and therefore not require arbitrary indexing in the generated code. Or you could imagine compiling to a small switch with a case for each valid index and a default of panicking. You’re not wrong that this is more complicated and there’s almost definitely lower hanging fruit but iirc inlining tends to only happen inside a crate with the exception of generic functions and functions explicitly marked inline, while LTO which would allow that to not be the case is still pretty slow and expensive.

                                              1. 1

                                                Since it’s passed by value, it could just be spilled to the stack if/when you need it. Since it’s a fixed small size, typical iteration should be able to be unrolled and therefore not require arbitrary indexing in the generated code. Or you could imagine compiling to a small switch with a case for each valid index and a default of panicking.

                                                The point is that spilling to the stack is what the current calling convention already does. Similarly, dense switch statements are generally compiled to a lookup table, which means that you’ll end up having to do a load from memory anyway (only it will be “cold” memory from the .data segment instead of “hot” memory like the stack).

                                                1. 1

                                                  Sure but now the decision to spill to the stack is deferred from the caller to the callee. If the callee doesn’t need it then you don’t pay for it. If it does then you’re really not paying that much over the current ABI.

                                            2. 6

                                              I don’t want to infer types from my code. I’d rather infer the code from the types. Types are the spec, they are small and low in expressiveness, code is big and has infinitely more degrees of freedom than types. The bug surface area is smaller with types.

                                              He has a good point - just because we can do something, doesn’t mean we haven’t got it arse about.

                                              1. 7

                                                What does “infer the code from the types” even mean in practice?

                                                1. 9

                                                  Have a look at this video of Edwin Brady demonstrating Idris2

                                                  Around 5m45s he starts showing the usual dependently typed definition of a vector, and how to define the append function. He shows the computer-assisted process for getting a skeleton definition with a hole, telling it to case-split the definition on the first argument, then fill in the holes based on the variables that are in scope and their types. Then he talks about how this process is so mechanical that it can be completely automated (for simple functions), and around the 10 minute mark, Idris autogenerates the definitions of append and zipWith just from their type declarations.

                                                  1. 8

                                                    He shows the computer-assisted process for getting a skeleton definition … Then he talks about how this process is so mechanical that it can be completely automated … Idris autogenerates the definitions of append and zipWith just from their type declarations.

                                                    I like Idris, or at least (since I don’t actually use it) the idea of Idris, and I like expressive type systems and leaning more on them… but this gives me much the same discomfort as the idea of using LLMs — or even old Java wizards — to generate boilerplate: could we (rather, could languages) make the generatable boilerplate unnecessary instead?

                                                    1. 11

                                                      In some cases we could and I argue definitely should. I’m very slowly working on a language towards that idea.

                                                      In other cases though, I wouldn’t call it boilerplate. Idris’ (and others) proof search is making a large tree (sometimes infinite) and choosing one of the nodes in that tree. It’s good to have it written down which node, rather than just “choose the first option” because proof search algorithms change across releases.

                                                      But it would be awesome to have an environment where you could say “here’s a bunch of constraints, please just use whatever you find” for the former cases.

                                                  2. 5

                                                    Often, the signature of a function alone hints at what it does. In some cases, it even determines the implementation completely:

                                                    const :: a -> b -> a
                                                    

                                                    Any function with this type signature must be the constant function that ignores it’s 2nd argument and returns the first.

                                                    1. 3

                                                      The concept of this is parametricity in case anyone wants to do some research.

                                                      Or if you want to count type inhabitants more generally: https://wabbit.blog/posts/fp-counting-type-inhabitants.html

                                                      1. 3

                                                        It’s easy to find simple examples. But how would you signify a binary search using types?

                                                        1. 4

                                                          Using dependent types, it’s possible to encode a function from vectors to values which includes a low and high bound, as well as a proof that the low bound is less than the high bound. This sort of helper would contain the bulk of the binary-search logic, and could be wrapped by a function which computes the bounds and drives the search.

                                                          1. 1

                                                            That sounds interesting. Have you ever seen anything like this done in practice? (even experimentally)

                                                            1. 1

                                                              I’ve seen it done in Idris, but I can’t find the source code. It’s also been done in Lean 4; see this example from their documentation.

                                                          2. 3

                                                            Claim wasn’t that it always works. Neither does type inference. Article was just stating a preference for one direction over the other.

                                                          3. 2

                                                            That’s not inherently true though? b could contain an a or be able to be converted into an a somehow e.g. b being a string that represents a numerical input from a user.

                                                            1. 6

                                                              In the sort of environment we’re talking about, it’s not possible to inspect the types of values at runtime; the function has to make its choice for all a and b types. Parametricity leads to the ability to compute the term from its type signature, and also to prove “theorems for free” about the term and type.

                                                              1. 1

                                                                To put it simply: In languages like SML, Haskell and Rust (and discounting “unsafe” code), it is inherently true that a -> b -> a must be a function like the one @jonahx described.

                                                              2. 1

                                                                That’s written as a Haskell type, where a and b are parametric type variables, not specific types. That is, the function can’t constrain those type variables to be any particular type, nor can it infer any relation between the two types. It must return an a for any types a and b that it is given.

                                                                In many languages this inference would still be wrong because raising an exception would also fulfill the type, but in Haskell, IIRC, raising an exception returns an IO a rather than just an a like in most languages.

                                                                I believe that even in Haskell it’s not entirely true - a function that infinitely recurses fulfills any return type, so you can implement the given type with straightforward infinite recursion. That sort of thing isn’t very useful, though, so when playing this code-inferred-from-type game folks typically discard that particular trivial solution. It also doesn’t infer to the right type (such a function would have inferred type a -> b -> c) even though it can fulfill it, which might be disqualifying in some sense.

                                                            2. 2

                                                              I came here to comment on Edwin Brady’s nifty Idris language, but I see someone already did

                                                          4. 6

                                                            If these bureaucrats would listen to us, they’d leave us alone. How far the hackers have fallen that they are grateful for being given slightly fewer instructions. As Spock said:

                                                            I object to you. I object to intellect without discipline. I object to power without constructive purpose.

                                                            1. 28

                                                              I disagree. The regulations that passed place obligations on companies making money from open source, but not on the upstream projects. That’s far better than being left alone because it requires the companies that benefit from open source to also invest in it, which addresses a market failure.

                                                              1. 1

                                                                The regulations that passed place obligations on companies making money from open source, but not on the upstream projects.

                                                                We’ve got a license for that. It’s called GPL (or AGPL), which forces the release of additions and modifications to the source code.

                                                                That’s far better than being left alone because it requires the companies that benefit from open source to also invest in it, which addresses a market failure.

                                                                There is no market failure: People put their code on the internet for free and companies use it. At no point in time does the market fail in this entirely voluntary exchange.

                                                                1. 6

                                                                  We’ve got a license for that. It’s called GPL (or AGPL), which forces the release of additions and modifications to the source code.

                                                                  These require passing changes forward but:

                                                                  • Don’t require upstreaming anything.
                                                                  • Don’t require anything if you build a product around them without modification.

                                                                  There is no market failure: People put their code on the internet for free and companies use it. At no point in time does the market fail in this entirely voluntary exchange.

                                                                  When heartbleed made the entire Internet vulnerable because everyone used OpenSSL but no one paid for proper security reviews, I’d call that a market failure that had huge implications for critical infrastructure.

                                                                  1. 1

                                                                    Don’t require upstreaming anything.

                                                                    Why should anyone be compelled to upstream? With (A)GPL, the source code modifications and additions are published; the upstream maintainer can decide for themselves how they proceed with their project.

                                                                    Don’t require anything if you build a product around them without modification.

                                                                    So what? Anyone is free to clone the code and build a product. That is exactly the premise of Open Source: Apart from the license like GPL or MIT, there are no strings attached - you can do whatever you please.

                                                                    When heartbleed made the entire Internet vulnerable because everyone used OpenSSL but no one paid for proper security reviews, I’d call that a market failure that had huge implications for critical infrastructure.

                                                                    That’s like saying that the Tacoma Narrows Bridge collapse was a market failure. If only they paid the engineers more, they would have pored more over this problem. Heartbleed was a programming and engineering failure. I am certain that we will enjoy more such bugs in the future, regardless of what regulation the EU passes. I’m also curious about what unintended consequences the laws will have. I can already see companies going through the motions without actually caring one single bit about real security.

                                                                  2. 4

                                                                    the term “market failure” is meaningless if the market is just supposed to do what it does and serve no societal purpose. but don’t you think the government has some obligation to society in what it does, including when it creates markets?

                                                                    1. 1

                                                                      the term “market failure” is meaningless if the market is just supposed to do what it does and serve no societal purpose

                                                                      The whole point of the market is to serve a societal purpose, namely the voluntary exchange between consenting parties, in which they trade something they value less for something they value more, benefitting all participants. That is what “the market is just supposed to do”. In other words, the term “market failure” is indeed meaningless.

                                                                      but don’t you think the government has some obligation to society in what it does, including when it creates markets?

                                                                      The government doesn’t create markets. People who produce things and trade create markets. A government can create an environment that facilitates or impedes the productive activities that take place on a market (often, the latter). Everything that is solved through the government involves, at some point, armed men with guns that will lock non-compliant people into cages. It is the final level of escalation, raw violence, when nothing else works. As a consequence, we should reserve such “solutions” for the severest problems that are plaguing our society. All that aside, you are speaking about the government having some obligation, but it is exactly the other way around: It is always people who are compelled to do something, be it by their morals or by the law. The government is a societal construct and it has the purpose that we assign it. The prevailing opinion seems to be that the government should be involved in every aspect of our lives, which is why laws are passed at a rate that surpasses anyone’s capability to read and understand them.

                                                                      1. 1

                                                                        The whole point of the market is to serve a societal purpose, namely the voluntary exchange between consenting parties, in which they trade something they value less for something they value more, benefitting all participants. That is what “the market is just supposed to do”. In other words, the term “market failure” is indeed meaningless.

                                                                        It doesn’t make sense to say something has a purpose if it can’t possibly fail at that purpose. I think you are just positing that it’s beneficial for society if people make voluntary transactions, for a very particular sense of “voluntary,” and when that happens it’s called a market.

                                                                        The government doesn’t create markets. People who produce things and trade create markets. A government can create an environment that facilitates or impedes the productive activities that take place on a market (often, the latter). Everything that is solved through the government involves, at some point, armed men with guns that will lock non-compliant people into cages. It is the final level of escalation, raw violence, when nothing else works. As a consequence, we should reserve such “solutions” for the severest problems that are plaguing our society. All that aside, you are speaking about the government having some obligation, but it is exactly the other way around: It is always people who are compelled to do something, be it by their morals or by the law. The government is a societal construct and it has the purpose that we assign it.

                                                                        If the government has the purpose we assign it, how is that any different than saying the government is obligated to fulfill that purpose? Why do I have it backwards but when you say it it’s right?

                                                                        I thought we were talking about something real. Has the type of market you have in mind ever existed? As far as I know, there is no historical precedent for people agreeing on who rightfully owns what such that a government is not necessary to enforce a particular notion of “voluntary” in order to create a market.

                                                                        1. 1

                                                                          I think you are just positing that it’s beneficial for society if people make voluntary transactions

                                                                          I am, yes!

                                                                          for a very particular sense of “voluntary,”

                                                                          I honestly can’t follow you. What part of “voluntary” is unclear?

                                                                          If the government has the purpose we assign it, how is that any different than saying the government is obligated to fulfill that purpose? Why do I have it backwards but when you say it it’s right?

                                                                          How does the government do something? It is a framework to obligate people to do it. I guess the difference I want to highlight is that, when your or I don’t fulfill our obligations, we face consequences. If the government doesn’t fulfill its purpose, what are you going to do? Imprison an abstract concept? Have the taxpayer pay a fine? You are right that colloquially it is referred to as an obligation, but it’s a very different kind of obligation. It’s like saying “mathematics has the obligation to help us model physical phenomena”, which is wrong, it’s designed to do that.

                                                                          Has the type of market you have in mind ever existed?

                                                                          It’s a gradual thing. To show a stark contrast, that type of market existed more in the US than in the Soviet Union. You notice it immediately by the amount of red tape you’re facing when you want to trade or produce things or start a company.

                                                                          1. 3

                                                                            I honestly can’t follow you. What part of “voluntary” is unclear?

                                                                            If you own the land I grew up on, you have a gun, and the only work I can find to feed myself is to work the land you own, is it “voluntary” for me to agree to work for you so I can live? Presumably if you pointed the gun at me while offering me work, it would not be voluntary, but if you don’t point the gun at me the consequence of not working for you is still death.

                                                                            If I don’t agree with your claim on my land, and I eat some of the food I am growing on my land without your permission, am I committing aggression against you? What if I go hunting in the woods next to the farm that you also claim as your land?

                                                                            It’s a gradual thing. To show a stark contrast, that type of market existed more in the US than in the Soviet Union. You notice it immediately by the amount of red tape you’re facing when you want to trade or produce things or start a company.

                                                                            Why would that be the measure of whether a government is necessary to create a market? Why not look at things like police per capita, punishments for property theft, or eviction law? A country where everyone agrees on property rights and what it means for a transaction to be voluntary would not need laws or punishments to enforce those things.

                                                                            As a settler colonial slave state, the U.S. is a prime example of the state enforcing a completely illegitimate regime of property rights, built on violent expropriation of land and wealth derived from forced labor. The relatively peaceful market only emerged after a long period of extreme violence to make people understand the harsh consequences for contesting the terms of government-enforced property rights.

                                                                            1. 3

                                                                              Property disputes are as old as the concept of property. They have to be resolved with a lot of care and wisdom. None of that invalidates the idea that it’s desirable to have a society where most transactions are voluntary.

                                                                              1. 2

                                                                                Yes, but you and I may have very different definitions of “voluntary,” so it’s a questionable goal when that point is glossed over. It’s also why governments are necessary to create markets.

                                                                                Property disputes are as old as the concept of property.

                                                                                Exactly, and that’s why prehistoric stateless societies were not market economies, and governments predate markets.

                                                                2. 17

                                                                  I find that a poor statement. The reason the CRA extends to the Open Source scene is not because of Hackers. It is because of companies that grab software for free, ship it to customers poorly and don‘t care about secure software.

                                                                  The problem with the initial draft of the CRA is that it would extend to hobbyists, non-profits and non-commercial open source. That feedback was taken and heard. The initial CRA was well-intended, with un-intended side effects.

                                                                  The advantage of the new CRA is that it encourages making sure the software you ingest as a business is maintained well, which is of huge interest for open source people that want to make a living with what they do.

                                                                  Also, having been in conversations with some of the people that were in the discussions with the EU there, there‘s also the sentiment on the bureaucrats side that the open source scene never shows up unless they want to complain or want money. Politics lives by being active.

                                                                  But that aside: if you want to be an old-school hacker that just codes away: that was the case before and there‘s been active groups like the FSFE and the open source business alliances that made sure it stays this way by explaining it to people that understood the problem and changed their drafts(!).

                                                                  1. 13

                                                                    If these bureaucrats would listen to us, they’d leave us alone.

                                                                    Depends what you actually mean by “us”. If that’s “us FOSS producers”, they mostly did. If that’s “us software developers”… we as an industry routinely release crap software full of bugs and vulnerabilities, with little to no accountability. Users are regularly annoyed and even hurt as a result (loss of time, loss of data, malware…), and these last couple decades we’ve seen precious little actual innovation to counterbalance that — except in hardware, but those folks basically complied with the CRA before it was even drafted.

                                                                    We had it coming.

                                                                    1. 3

                                                                      Is this a reply to the parent or to my comment?

                                                                      (FWIW, I fully agree, the target was industry malpractice, not individual people and projects writing liberal software)

                                                                      1. 1

                                                                        Dammit, it was for the parent comment from @BenjaminRi. I keep getting tripped up by the counter intuitive placement of the “reply” link, which forces me to to scroll up before I can actually reply.

                                                                    2. 9

                                                                      Too much of society runs on software for hackers to have no instruction. People’s lives depend on the code we write and we do in fact have ethical obligations to the rest of the world. It’s not the 90s and software isn’t punk anymore, it’s the incumbent and as a profession we need to start accepting that fact and behaving appropriately. Doctors aren’t allowed to do surgery without washing their hands anymore because that’s bad, it was only a matter of time before programming picked up similar requirements.

                                                                      1. 6

                                                                        I don’t know what you mean by “grateful for being given slightly fewer instructions,” but this smacks of an ignorance of how power operates, for example the fact that appropriating the fruits of labor necessary to live constitutes power over people who will inevitably try to live.

                                                                      2. 2

                                                                        I’m curious as to how remapping solves this problem? I was under the impression that CPU prefetch was based around virtual addresses and not physical ones. And fragmentation was also an address space problem, not a problem with the underlying memory.

                                                                        1. 2

                                                                          (you accidentally double posted)

                                                                          1. 2

                                                                            The assumption here is that you’ll run out of physical pages long before virtual address space, so physical memory waste caused by fragmentation is the problem to solve.

                                                                            This gives you another level of defense against fragmentation. Imagine the first half of one virtual page is free, and the second half of another virtual page is free. You can map both of those to the same physical page and never allocate that free virtual space. No savings on virtual fragmentation, but physical memory usage is halved.

                                                                            1. 1

                                                                              I’m curious as to how remapping solves this problem? I was under the impression that CPU prefetch was based around virtual addresses and not physical ones. And fragmentation was also an address space problem, not a problem with the underlying memory.

                                                                              They are concerned about applications running out of memory (and maybe having to swap), not making the job easier for the CPU cache. The paper is clearer about that:

                                                                              Memory consumption is a serious concern across the spectrum of modern computing platforms, from mobile to desktop to datacenters. For example, on low-end Android devices, Google reports that more than 99 percent of Chrome crashes are due to running out of memory when attempting to display a web page. On desktops, the Firefox web browser has been the subject of a five-year effort to reduce its memory footprint. In datacenters, developers implement a range of techniques from custom allocators to other ad hoc approaches in an effort to increase memory utilization.

                                                                              1. 1

                                                                                I was under the impression that CPU prefetch was based around virtual addresses and not physical ones

                                                                                You mean the automatic prefetcher? I would be surprised if that were the case. For l1, maybe, but by the time you hit l2, everything is physical. Either way, though, I don’t see why that would matter.

                                                                              2. 1

                                                                                I’m curious as to how remapping solves this problem? I was under the impression that CPU prefetch was based around virtual addresses and not physical ones.

                                                                                1. 1

                                                                                  It is, but virtual address space is enormous (64 bits) and fragmenting it to the point of unusability is not really possible (heck, ASLR does this on purpose for security hardening) Physical memory isnt, it gets meaninfully fragmented pretty easily. If you don’t have enough contiguous space in physical ram, you can’t make an allocation, even if that space does exist in virtual address space. At least not without page table trickery much like what this is doing.

                                                                                2. 7

                                                                                  Some of these findings are pretty damning for Rust, given how high and mighty everybody likes to sound about Rust’s safety guarantees. If reasonably well-written C++ is safer than Rust in many cases, I doubt it’s a good idea to “experiment” with Rust for FreeBSD. It would be a high cost to pay for this particular project if it turns out in the long run that it doesn’t help much. They have manpower problems enough as it is.

                                                                                  1. 24

                                                                                    If reasonably well-written C++ is safer than Rust in many cases

                                                                                    I don’t think it is, in most cases. There are corner cases around interfacing with things outside of the abstract machine where the C++ code will be safer than naive Rust, but in most cases Rust code will be as safe or safer than well-written modern C++.

                                                                                    They have manpower problems enough as it is.

                                                                                    I think this is where the economics gets interesting. There are a lot of enthusiastic Rust programmers. There are very few people under 40 that want to write C code. Probably more that want to write C++ code, but few that are as enthusiastic as the ones that really want to write Rust. It may be that the pool of Rust programmers that want to contribute to an open-source OS is larger than the total number of current FreeBSD developers.

                                                                                    1. 5

                                                                                      in most cases Rust code will be as safe or safer than well-written modern C++.

                                                                                      Ah, then I sort of misunderstood your take-away point in the mail. Thanks for clarifying!

                                                                                      I think this is where the economics gets interesting. There are a lot of enthusiastic Rust programmers. There are very few people under 40 that want to write C code. Probably more that want to write C++ code, but few that are as enthusiastic as the ones that really want to write Rust. It may be that the pool of Rust programmers that want to contribute to an open-source OS is larger than the total number of current FreeBSD developers.

                                                                                      But it would be risky to switch to Rust just to attract new potential contributors if it means alienating the existing contributor base (most of whom are probably over 40 and well-versed in C). Especially if it means increased churn, higher maintenance burden and if it all turns out to be a flash in the pan it’s wasted effort.

                                                                                      1. 9

                                                                                        But it would be risky to switch to Rust just to attract new potential contributors if it means alienating the existing contributor base (most of whom are probably over 40 and well-versed in C).

                                                                                        Many are over 40, and that’s part of the problem. The project has an aging contributor base. At the moment, I think we’re still attracting new ones at replacement rate, but the number that are dying or leaving due to burnout is high. I’ve not contributed much for quite a long time (I am over 40, for what it’s worth), in part because most of the things I’d want to improve are C. At this point, C is basically the subset of C++ that you should actively avoid when writing C++. I write code with a much lower error rate and get a lot more done per line of code in C++ than C and so it’s difficult for me to motivate myself to write C.

                                                                                        1. 5

                                                                                          But it would be risky to switch to Rust just to attract new potential contributors if it means alienating the existing contributor base

                                                                                          This is a tricky one. Because of course you’re right, losing all existing contributors would be a disaster. And if no one new comes anyway even worse.

                                                                                          But, given some time, getting no new contributors is an even more permanent disaster.

                                                                                          So, some kind of balance of priorities is needed (I’m not saying rust in this project is definitely on one side or other of the balance, I don’t know enough of the contributors to have a stake there).

                                                                                        2. 4

                                                                                          Can I just say that it’s an amazing part of this community that a third party can repost a technical analysis and there’s decent odds the writer will see it and be available in the comments to clarify points? It’s really something special and one of the things that the Internet really excels at.

                                                                                        3. 18

                                                                                          I don’t see anything here that’s ‘damning’ for Rust. The criticisms seem to boil down to a few key parts, several of which are misunderstandings by the author:

                                                                                          First, it’s quite hard to find competent Rust developers.

                                                                                          It’s a new language. Not surprising and not fundamental.

                                                                                          Neither Rust nor C++ guarantee safety

                                                                                          A lot of what’s being said here seems to be based on several key misunderstandings of what Rust is trying to achieve and why. No language can ‘guarantee safety’ by the overly specific definition of here: even superficially safe languages like JavaScript are still relying on the runtime that executes them, written using unsafe code, to not do something nasty accidentally. The only difference with a language like Rust is that it’s made the decision to move that unsafety out of a complicated and difficult to audit runtime and into an easier to audit standard library. This is a safety win. What’s important here is that unlike C++, Rust has a well-defined and easy to discriminate safe subset (i.e: it’s easy to tell what does and does not need more careful attention during a code review).

                                                                                          There was a paper a couple of years ago that found a lot of vulnerabilities from this composition.

                                                                                          I’m not sure what’s being referenced here, but if two “safe” abstractions do not compose safely, then at least one of them is, in actual fact, unsafe. Rust has pretty rigorously ironed out a lot of the kinks in its safety model now, and it’s even been formally proven that composition retains safe properties.

                                                                                          Moving the check into the unsafe block fixed it, but ran counter to the generic ‘put as little in unsafe blocks as humanly possible’ advice that is given for Rust programmers.

                                                                                          This, too, is a fundamental misunderstanding. Rust’s safety guarantees are semantic, and when you take it upon yourself to read raw bytes and interpret them as well-formed values in the Rust type system, checking that they are well-formed is the unsafe code that needs careful audit. What would be the point of an unsafe block if it didn’t actually demarcate the condition that enforces correct behaviour? It seems like the author believes that an unsafe block can be read simply as a ‘hold my beer’.

                                                                                          When I looked at a couple of hobbyist kernels written in Rust, they had trivial security vulnerabilities due to not sanitising system call arguments.

                                                                                          I think this is a reasonable criticism of the kernels in question, but not of Rust: to even invoke (or receive) a syscall to begin with, you’re moving outside the domain of Rust’s safety model (and this must be explicitly annotated as such with an unsafe block). Ergo, it is your responsibility to ensure that whatever invariants that are required to maintain safety are maintained. If you want a language that can automatically understand what a syscall is and what invariants it has without first telling the language what they are… well, you’re going to be waiting for a very long time. Even theorem provers still need you to state your priors.

                                                                                          If there is a valid piece of information to pull out of this, it might be “the promises that Rust makes do not necessarily align with the most common safety issues present in kernel code”. I think that’s a much more reasonable claim to make, although it still does fly in the face of a lot of existing research about memory safety.

                                                                                          1. 3

                                                                                            if two “safe” abstractions do not compose safely, then at least one of them is, in actual fact, unsafe

                                                                                            Strictly speaking, this should be “unsound” rather than “unsafe”. In the context of Rust, safe/unsafe is an API-level guarantee (or lack thereof), if a safe API does not uphold its requirements, it’s unsound.

                                                                                            1. 2

                                                                                              There’s a crowd within the Rust community that considers unsafe to be poorly named and would prefer something like unchecked. I’m not sure I’ve decided either way.

                                                                                              1. 2

                                                                                                I mean sure but that’s not really relevant.

                                                                                                A safe rust API which does not uphold the requirements of its unsafe callees is unsound, not unsafe, even if you decide to call unsafe something else.

                                                                                          2. 7

                                                                                            People in that thread are not shy about pointing out Rust’s imperfections, but I don’t see any of them claiming that “well-written C++ is safer than Rust in many cases” ? At best, the linked post gives a fairly niche (RTOS on special hardware) example where C++ was as safe as Rust.

                                                                                            The point of experimenting is that you don’t put all your resources in it, and the consensus on that thread seems to be that they’ll introduce Rust at the edges, without committing fully yet. The hope is that Rust will actually help their manpower issues, by being a more productive language and by attracting new contributors. Worth a try.

                                                                                          3. 2

                                                                                            Most of the software in the world was written at a time when there was no process of code review. For private codebases, I have seen no evidence of an improvement in the resulting software. While their use in codebases that have public (unknown) contributors has merit, can anyone point to evidence that their use in private codebases results in a better software application?

                                                                                            1. 3

                                                                                              I think that depends on how you define private. Is the Google codebase private? While it’s not open source it’s modified by a huge number of people with extremely varying levels of proficiency. Compare that to a two man team who are at roughly the same skill level or have dedicated areas of expertise that they are responsible for. I do think that we tend to overoptimize for the Google scale because scalability is a buzzword but I wouldn’t be surprised if the inflection point where code reviews become a net benefit is somewhere around the 10-20 programmers region. It seems like at that point it would be hard to have the same level of trust and coordination that the two programmer example can maintain. It’s probably something that would merit further study but is in the category of “practices” where cargo culting is unfortunately common and the methodology tends to be less than stellar.

                                                                                            2. 4

                                                                                              Isn’t this just container_of, which is part of lots of C codebases?

                                                                                              The Linux kernel has a type-safe version of it IIRC.

                                                                                              1. 3

                                                                                                Yeah, I’m pretty sure that’s the equivalent in C.

                                                                                                1. 2

                                                                                                  Yep, though IMO it’s worth calling out since I don’t think I’ve seen anything like it in any language besides C/C++. Not many things these days allow intrusive pointers!

                                                                                                  1. 2

                                                                                                    Yep, it’s basically a offset-of construct, but it’s only legal in C, you cannot use it in C++.

                                                                                                    What’s special about it in Zig is that regular Zig structs have no well defined layout, and fieldParentPtr requires no use of semi-illegal pointer arithmetic.

                                                                                                    Cool thing about no well defined layout is that the compiler can reordner fields based on the optimal packing with regards to alignment, but also have the user keep logical orderings.

                                                                                                    Explicit layouts are supported with extern struct.

                                                                                                    Using fieldParentPtr makes funny constructs like LinkedList(void) really useful as you can store heterogeneous elements in a linked list, and access the parent elements via fieldParentPointer

                                                                                                    1. 2

                                                                                                      Wait why isn’t this valid C++? Is it a pointer provenance thing?

                                                                                                      1. 1

                                                                                                        You cannot use offsetof only on POD types, so nothing that uses inheritance, virtual functions or constructors

                                                                                              2. 1

                                                                                                This is only removing the libc interface to do an arbitrary syscall, right? The syscalls themselves are still implemented in assembly with something like

                                                                                                mov eax,1
                                                                                                syscall
                                                                                                

                                                                                                Or with a int 0x80. Right? So if an attacker can still do this, what’s the advantage?

                                                                                                1. 13

                                                                                                  OpenBSD blocks syscalls not made through libc

                                                                                                  1. 4

                                                                                                    Not quite. OpenBSD blocks system calls made from pages that are not explicitly marked as valid sources from system calls. Most of libc is not allowed to make system calls (if you find a weird bit of x86 machine code that looks like a syscall instruction if you jump into the middle of it, it still can’t be used to make system calls). If you have your own system call dispatcher, you can mark the page that contains it in the same way.

                                                                                                    1. 2

                                                                                                      Right, assuming your program isn’t being runtime linked by ld.so(1), so in practice for programs in base this isn’t possible after calling main.

                                                                                                  2. 12

                                                                                                    From what I understand the kernel will check the origin of the syscall and if it’s outside of where it expects it to be it’ll just refuse to service it. Therefore the only way to actually perform the syscall is to go through libc.

                                                                                                    1. 2

                                                                                                      Combine this with a way to only load the parts of libc that your application actually uses, and it should be a pretty nice improvement.

                                                                                                        1. 1

                                                                                                          a way to only load the parts of libc that your application actually uses

                                                                                                          That’s static linking. You really can’t load portions of a shared library, because a) it’s shared, and b) other programs might use other parts of said shared library.

                                                                                                          1. 2

                                                                                                            Even if other programs use different parts of the library I could elect to only map specific parts to my memory space. It’d be trickier to handle libraries loaded after entering main().

                                                                                                    2. 2

                                                                                                      I wish C++ would add unsigned types that are one bit smaller than a power of two: 7, 15, 31, and 63-bit values. For a lot of things, like array indexes, I want an unsigned type because a negative number is a domain error, but I don’t actually need the full range of a power-of-two type.

                                                                                                      1. 3

                                                                                                        I like the uncommon language feature of integer types with explicit ranges baked in, like int<1…100>. It mostly seems to be found in Pascal-derived/inspired languages.

                                                                                                        Of course runtime range checking of these types is expensive, and proper compile-time type analysis is awkward.

                                                                                                        1. 1

                                                                                                          Yeah I’d imagine the promotion rules would be really obtuse and what type are the intermediate values and aaaaaahhhh this is hard. I do not want to specify a type every time I use a variable.

                                                                                                      2. 13

                                                                                                        My summary: when curl is given a specific range of numbers an integer overflow could result in making requests faster than expected. This is described as a DoS.

                                                                                                        This sounds like unexpected behavior, but what turns that into a security flaw?

                                                                                                        The input required is sort of bonkers, and what is providing that input? Untrusted users plugging arguments into curl sounds like a security flaw in the “upstream” application.

                                                                                                        Regardless, that severity is crazy.

                                                                                                        1. 4

                                                                                                          That and many other are indeed crazy, since the calculation is typically done without an operational context (NVD’s argument is they’d rather play safe when it comes to security issues, so scores are higher where context is lacking).

                                                                                                          I would rather prefer NVD contact the vendor, seek clarification, and update the CVSS score (I don’t think this happens).

                                                                                                          1. 3

                                                                                                            I would rather prefer NVD contact the vendor, seek clarification, and update the CVSS score (I don’t think this happens).

                                                                                                            This would be helpful. What would be even more helpful is if they had some sort of indication that they took this “worst case scenario” route.

                                                                                                            It would have been way better to have said “we think this is a 9.8 CVSS score based on extremely incomplete information. See here for our policy in this situation.” It’s almost like a single number with no context isn’t enough bits of information…

                                                                                                            1. 1

                                                                                                              They actually do, see https://nvd.nist.gov/vuln-metrics/cvss (relevant part reproduced below):

                                                                                                              Incomplete Data

                                                                                                              With some vulnerabilities, all of the information needed to create CVSS scores may not be available. This typically happens when a vendor announces a vulnerability but declines to provide certain details. In such situations, NVD analysts assign CVSS scores using a worst case approach. Thus, if a vendor provides no details about a vulnerability, NVD will score that vulnerability as a 10.0 (the highest rating).

                                                                                                              However, since they always provide a Base score, without factoring in the Temporal or Environmental metrics, it will necessarily be calculated using the “worst case scenario”.

                                                                                                              NVD then continues to mention this:

                                                                                                              Collaboration with Industry

                                                                                                              NVD staff are willing to work with the security community on CVSS impact scoring. If you wish to contribute additional information or corrections regarding the NVD CVSS impact scores, please send email to nvd@nist.gov. We actively work with users that provide us feedback.

                                                                                                              Daniel must have contacted them, which is why the CVE now is marked Disputed.

                                                                                                              1. 3

                                                                                                                I think you may have misunderstood me. I was saying that that information should be attached to the CVSS number itself. So instead of saying e.g. “this vulnerability is CVSS 9.8” you’d say “this vulnerability is a CVSS 9.8, low-confidence” (low confidence meaning, NVD didn’t have a lot of information to work with). This would help to distinguish from “CVSS 9.8 high-confidence” (NVD did have all the info they needed, which at least somewhat implies that this represents a true threat and not a theoretical worst-case scenario NVD made up).

                                                                                                                I will admit though that I didn’t read that link, just the parts you quoted, so maybe I just don’t know enough about CVSS scoring.

                                                                                                                1. 2

                                                                                                                  Ah, okay. Yes, specifying the confidence level next to the score is definitely useful in effective risk mitigation. I will definitely welcome this change.

                                                                                                          2. 1

                                                                                                            It’s definitely a bug since triggering UB is uh… not great. But yeah since the input is given by the person running curl it’s not meaningfully bad and it seems like current compilers don’t turn the UB into a security vulnerability?

                                                                                                          3. 3

                                                                                                            contrary to appearances, it’s not really a compiler bug: it’s a processor bug.

                                                                                                            How so? The FPU does exactly what it’s supposed to do, according to the spec. The compiler is the one generating bad code based on wrong assumptions about the hardware.

                                                                                                            1. 2

                                                                                                              It’s really not though. C just has super loosely defined floating point. Technically it is conformant. I’d call it a bug in the C standard or a bug in the programming model when applied to non-sse x86

                                                                                                              1. 2

                                                                                                                Surely though equality must be deterministic, right? If a == b is true once, and I don’t change a or b, it shouldn’t evaluate to false later on.

                                                                                                                1. 2

                                                                                                                  Not if the spec is worded loosely enough. IMO this IS a compiler bug because it’s the compiler’s job to make intelligent decisions about the things the spec leaves implementation-defined.

                                                                                                            2. 6

                                                                                                              Has anyone ever had anything mysteriously work better because of the x87’s 80-bit floats? I honestly thought that modern compilers stopped using it ages in favor of just using SSE everywhere. IIRC x86_64 calling conventions tend to put floats in the SSE registers anyway, so, why would you want to bother with the x87?

                                                                                                              1. 2

                                                                                                                Given that the gcc docs specifically mention which microarchitectures support SSE I assume it’s being conservative and assuming the SSE instructions aren’t available. But that’s just a guess. As for the x87, it was a good idea back when people were programming in ASM. Programmers would understand the quirks of it from reading the ISA manuals to figure out the programming model. But C presents an abstraction where types have specific fixed widths regardless of whether they’re in registers or in memory. In order to maintain that model it would require the compiler to be really pessimistic and leave a lot of perf on the table and technically C doesn’t specify enough about floating point to make it illegal for them to make the optimizations.

                                                                                                                https://gcc.gnu.org/onlinedocs/gcc/x86-Option s.html

                                                                                                                1. 1

                                                                                                                  SSE2 is available on all x86_64 systems, it’s part of the standard. The question is, why is it worth using the x87 directly at all? I would dearly hope that SSE/MMX/AVX could do everything it does, better. But I don’t know for real. But some playing around with Godbolt shows it using SSE for trivial floating point math.

                                                                                                                  And technically the C standard doesn’t make it illegal for invoking gcc to delete my hard drive either, but I would hope that behavior would be regarded as a bug by the gcc team.

                                                                                                                  1. 2

                                                                                                                    SSE2 is available on all x86_64 systems, it’s part of the standard.

                                                                                                                    The OP was having trouble with the 32-bit build specifically. In that context, does it make more sense to fall back onto x87? (This is a genuine question. I’m out of my depth when it comes to low-level stuff like this.)

                                                                                                                    1. 1

                                                                                                                      Oh fak, I forgot that part of the equation. Yeah, it makes a lot more sense then. Alas.

                                                                                                                    2. 1

                                                                                                                      The bug only manifested on 32 bit systems because 64 bit guarantees SSE. And I think it’s clear at this point that gcc is going to stretch the standard as far as they can. They’ve been more than willing to go much further than this and this is relatively minor.

                                                                                                                2. -9

                                                                                                                  I didn’t know what a tarpit was and hooo boy!, neither does the author

                                                                                                                    1. 3

                                                                                                                      That’s how I interpreted it, as the first paragraph ends with talking about increasingly wasted effort.

                                                                                                                    2. 16

                                                                                                                      I don’t think Urban Dictionary is a reasonable way of trying to define things in “technical” articles.

                                                                                                                      I’m inclined to either guess it was meant quite literally as “you’ll be stuck in there if you go in” or maybe a reference to https://en.wikipedia.org/wiki/Turing_tarpit

                                                                                                                      1. 1

                                                                                                                        It sounded like a “common usage”, and I go to urban dictionary for common terms. TBH I’ve never heard of any of the usages that other people posted here (except the place in LA, which it clearly isn’t referring to).

                                                                                                                        What’s clear is that the definition is not clear to anyone. And for that, including it here was a mistake given the unknown size of the audience and the fact that no one agrees what it means.

                                                                                                                        1. 2

                                                                                                                          I’m not a native English speaker, but I’ve heard of it specifically in the networking sense.

                                                                                                                        1. 7

                                                                                                                          Urban Dictionary is full of bogus definitions. Look up a random noun (e.g. “candle”) and chances are the top definition is some sexual slang that no one has ever heard of, let alone would snigger at when someone uses it in the common sense.

                                                                                                                            1. 6

                                                                                                                              Tar-pits around the world are great dig sites, because animals get stuck in them and die, but are preserved by the tar. The one in LA is one of the most (if not the most) significant such sites.

                                                                                                                              Was there was a period where paleontology was write larger in the collective consciousness? I feel like it was a widely thing when I was a kid around 1995.

                                                                                                                              1. 3

                                                                                                                                Jurrasic Park was a big thing back then and got people interested in that kinda stuff. It’s actually really interesting how you can map pop culture stuff to the kinds of careers people want to get into later in life.

                                                                                                                          1. 4

                                                                                                                            This reads like a parody of itself, but appears to be real. Neat.

                                                                                                                            Intel® APX doubles the number of general-purpose registers (GPRs) from 16 to 32

                                                                                                                            Compiler enabling is straightforward – a new REX2 prefix provides uniform access to the new registers across the legacy integer instruction set. Intel® AVX instructions gain access via new bits defined in the existing EVEX prefix. In addition, legacy integer instructions now can also use EVEX to encode a dedicated destination register operand – turning them into three-operand instructions

                                                                                                                            we are adding PUSH2/POP2 instructions that transfer two register values within a single memory operation

                                                                                                                            Intel® APX adds conditional forms of load, store, and compare/test instructions, and it also adds an option for the compiler to suppress the status flags writes of common instructions. These enhancements expand the applicability of if-conversion to much larger code regions

                                                                                                                            I am somewhat concerned about the effects on codesize. Of course skipping spills and moves will help there, but those aren’t what really hurts x86—it’s the prefix bytes, and this is making matters worse there.

                                                                                                                            1. 4

                                                                                                                              I am somewhat concerned about the effects on codesize.

                                                                                                                              They say this in the 4th paragraph:

                                                                                                                              While the new prefixes increase average instruction length, there are 10% fewer instructions in APX-compiled code, resulting in similar code density as before.

                                                                                                                              1. 2

                                                                                                                                Well if you don’t use these instructions you can just ignore them and your code size will stay the same. That said I actually laughed at the PUSH2 POP2 instructions. That bit is silly.

                                                                                                                                1. 4

                                                                                                                                  They are copying the AARCH64 instructions that do the same thing.

                                                                                                                                  1. 4

                                                                                                                                    As someone said on fedi: “(A)arch64 (P)referred e(X)tension”

                                                                                                                                    1. 3

                                                                                                                                      This. AFAICT this is actually a silly-looking sweet spot between complexity and utility. If you have “take a parameter and push/pop multiple registers” function like 32-bit ARM then the CPU doesn’t know how many memory transfers you will need, what data dependencies it will have, and how long it will actually take to execute; the CPU always has to figure this out on its own. IIRC there’s also weird edge cases like, what does the CPU do when a page fault or interrupt occurs partway through a multiple-element push/pop? I’m sure I’ve seen some 32-bit ARM doc that discusses the gotchas but can’t find it right now. Maybe it’s in their system programming guide?

                                                                                                                                      With larger-but-fixed-size push and pop instructions all these questions have very specific answers that are coded into the hardware, it never has to figure out what resources it needs to execute. Page faults can’t happen ‘cause the stack always needs to be 16-byte aligned when the instruction executes, on interrupt it only ever needs to be able to buffer or roll-back max 2 memory operations, etc. I’m a little surprised there’s no PUSH4 or POP4 instructions, but it seems safe to assume in AARCH64 at least that they tried it and decided it wasn’t worth it.

                                                                                                                                      (Pinging @david_chisnall for fact checking!)

                                                                                                                                      1. 2

                                                                                                                                        AArch64’s ldp and sdp require 16-byte alignment (32 on Morello), which guarantees that they are within a cache line (which means that they don’t impose any extra cache-coherency traffic) and within the same page. This is a big improvement for microarchitectures (though less useful for compilers). Each stp can be a single entry in a store queue and works roughly the same way that a normal store is.

                                                                                                                                        The stm and ldm instructions on AArch64 were great for compilers and simple microarchitectures. As you say, the biggest problems were restarts. If you take a page fault in the middle, you have to redo the operation, which means that you don’t have forward progress guarantees. The ldm instruction was especially painful because the PC was a GPR with AArch32, so it could also be a jump (this was great for code density, which is why the RISC-V code size working group has proposed a restricted but similar version as popret). The instruction has to be implemented as a complex state machine (or microcode).

                                                                                                                                        1. 1

                                                                                                                                          Thanks! Sorry for calling you out, but I don’t know many actual compiler backend implementors or researchers and so I tend to learn new things every time you say stuff.

                                                                                                                                          1. 2

                                                                                                                                            Oh, I forgot to say: x86 chips have some fun logic to allow atomics that span a page boundary. Last time I checked, this came with around a factor of 30 slowdown, but it means that they probably don’t need alignment requirements in POP2 / PUSH2. On recent Intel chips, this is fast for operations that span a cache line but not a page. This has the added benefit for Intel that it’s harder to emulate on Arm.

                                                                                                                                            1. 1

                                                                                                                                              Huh? Cache-line-spanning atomics are basically a deprecated feature on x86; they acquire a processor-wide lock, making them basically useless for implementing anything scalable, and on newer processors they can be configured to trap instead. (Look up ‘split locks’.) I suppose that in principle they could be useful for some asymmetric/amortised algorithms, like membarrier, but membarrier has some interesting properties that split locks don’t.

                                                                                                                                              Gather/scatter have a very simple solution to the problem of faulting in the middle of a load/store-multiple. I wonder what’s wrong with using that.

                                                                                                                                        2. 1

                                                                                                                                          Here’s an old comp.arch thread that has some thoughtful comments about block copy and LDM/STM instructions.

                                                                                                                                        3. 1

                                                                                                                                          Weirdly they also add instruction predication even though aarch64 largely removed it.

                                                                                                                                          1. 2

                                                                                                                                            It looks as if the predicated instructions are quite limited. AArch64 struggled with this. Conditional moves are a huge win for performance. Conditional branches are necessary. Other things are both useful in software and difficult to implement in hardware and it’s not clear cut whether they’re a win. The biggest difficulty was conditional loads: these are a huge benefit to a load of things but also very expensive in encoding space and hard to implement.

                                                                                                                                        4. 2

                                                                                                                                          It’s like CS freshman naming variables in their homework.

                                                                                                                                          1. 1

                                                                                                                                            The issue is compromise between code size and other factors. E.G. NF might increase codesize but also increase parallelism.

                                                                                                                                          2. 1

                                                                                                                                            This isn’t regressing the existing instructions, just some extra assets at your disposal. So given a smart enough compiler (which uses current-existing registers if possible, and only use the extended registers if there is a benefit, which is already the case as Intel already has instructions specialized for rax operand and instructions that are shorter when operating on the first 8 registers), I don’t see any reason that this would causes regression.

                                                                                                                                            And AFAIK the code density issue is much worse for RISC ISAs than x86, and IMO the prefix bytes are exactly what allowed x86 to yield better code density (as it provides specialized variants for more common use cases), rather than hurting it.

                                                                                                                                            1. 1

                                                                                                                                              See my other comment—the issue is tradeoffs between code size and other factors. These are very difficult to correctly balance globally. I do think that intel has done as good a job as can be expected given the hand they’ve been dealt, and that this will probably overall improve performance more than it harms it.

                                                                                                                                              Variable-length encodings can certainly achieve higher density, but x86’s is an inefficient encoding. For instance, every 64-bit op requires an extra prefix byte, because back in 2000, making 32-bit the default was the better play from a short-term business standpoint. If not for that, x86 would be able to do many basic reg-reg instructions in just 2 bytes, matching riscv-c. There are plenty of other things like this. (There is one standout that’s still pretty good: the addressing forms, and attendant lea. And that was even something they managed to improve for 64-bit.)

                                                                                                                                              1. 1

                                                                                                                                                Variable-length encodings can certainly achieve higher density, but x86’s is an inefficient encoding.

                                                                                                                                                Yes, I agree with that. It’s just that due to backward compatibility reasons it’s hard to redesign everything (maybe a binary rewriter could still work for most cases, but probably that would still leave a bunch of hard edge cases like self-modifying code)

                                                                                                                                                See my other comment—the issue is tradeoffs between code size and other factors.

                                                                                                                                                Sorry I read your other comment but still can’t see what you mean. In theory I do see the possibility that the “optimal code” with APX is larger than original code (as the advantage of APX outweighs the code density decrease), is that what you meant?

                                                                                                                                                1. 1

                                                                                                                                                  self-modifying code

                                                                                                                                                  Rosetta has no trouble with it.

                                                                                                                                                  still can’t see what you mean

                                                                                                                                                  The NF bit makes an instruction that would otherwise have set flags not set them, increasing parallelism by avoiding the need to track dependencies between instructions that would otherwise spuriously access flags. But setting the NF bit may require a larger prefix on an instruction that wouldn’t otherwise have needed it. So you have to trade off increased parallelism with larger code. And it is not obvious how to make this tradeoff.

                                                                                                                                                  EDIT: similarly, using a three-address instruction might take up more space than a move+op, but also avoid the need to allocate an extra rename+decode slot for the move.