HNHacker News
TopNewBestAskShowJobs

kbolino

2,384 karma · joined August 29, 2011

submissionscomments
kbolino··on Turn off Apple Intelligence on macOS 27 and get its disk space back
I'm not saying it was ever necessary for any of the computers (except maybe the passively-cooled MacBook Air), but that it was necessary for the phones, and so that design was carried over to the computers. There was a considerable gap (18 months) between the release of the M1 Mac Mini, the first Apple Silicon computer, and the release of the M1 Mac Studio, the first Apple Silicon computer with replaceable storage. I'm sure that time wasn't all spent on developing the storage socket, but much of it probably was spent on adapting to and utilizing the larger power and thermal budgets more generally.
kbolino··on Example.com just launched the biggest redesign in decades
It's not as easy to remember, but you can also do:

  data:text/html,<body%20bgcolor="white">
kbolino··on Turn off Apple Intelligence on macOS 27 and get its disk space back
Longer traces and contact connectors require more power and generate more heat to maintain the same level of signal integrity as shorter traces and soldered connections. All modern Apple hardware is downstream of its phone division. They don't make a specialty PC that runs a very different O/S (macOS instead of Windows) anymore, they make a scaled-up phone that runs a slightly different O/S (macOS instead of iOS) now.
kbolino··on Several vulnerabilities have been discovered in the Linux kernel
You may never have dug that deep into their respective documentation, and you may never have even heard of libcurl, but their superficial overlap in ability to download a single file from an HTTP server does not make them "trivially replaceable" with each other (in either direction).

Regardless, even if hypothetical product X could do everything cURL and libcurl do for most people, that wouldn't make cURL/libcurl much less load-bearing, any more than the existence of FreeBSD or Illumos make Linux less load-bearing.

kbolino··on Turn off Apple Intelligence on macOS 27 and get its disk space back
App bundles (what's inside most DMGs) and Windows executables are signed, have been for a long time, and are required to be, by the O/S, in order to execute "normally". Apple uses centralized PKI (the developer's key must be signed by Apple) while Microsoft uses distributed PKI (the developer's key must be signed by a code-signing CA who in turn is approved by Microsoft).
kbolino··on Several vulnerabilities have been discovered in the Linux kernel
> cURL developers just have NIH syndrome.

What does this even mean? cURL is one of the most load-bearing pieces of software in existence. It, and the Linux kernel, which takes a similarly dim view of the CVE system, are the inventors. "Not Invented Here" seems to imply that there is a vast body of peer work for them to draw on to resolve this problem, but who are their peers? As far as I can tell, the answer is something like "Microsoft and Apple", on the one hand, who exist in a totally different, mostly closed-source or at least closed-development, ecosystem, or something like "glibc and OpenSSL" on the other hand, which have their own storied CVE history.

kbolino··on Git 3.0's upcoming SHA-256 default will be a costly mistake
There's a lot of lingering "SSL is just for your bank" sentiment out there. There are other ways to achieve integrity protection than using TLS, and there are even ways to use TLS without WebPKI, but nobody is putting any effort into any of them, and so TLS+WebPKI is the solution, and no website is exempt, because it is fundamentally an infrastructure problem.
kbolino··on Git 3.0's upcoming SHA-256 default will be a costly mistake
The point I'm making is that "backwards compatibility" cannot rely on SHA-1 support being around forever. At some point, all uses of SHA-1 must go. This includes not only the interfaces between Git and external tools, but also Git's internal data structures. Past a certain point in the near future, there cannot be a two-tier Merkle tree system anymore; the SHA-1 tier has to be removed before long.
kbolino··on Git 3.0's upcoming SHA-256 default will be a costly mistake
Allowing both hash algorithms is, from a security standpoint, equivalent to just using the less-secure hash algorithm.

All repos need to end up using SHA-2 exclusively by the end. All tools that speak only SHA-1 need to be made incompatible intentionally. If the SHA-1/SHA-2 hybrid approach could allow that to happen, then it would be useful. If not, then it would just be a waste of time.

kbolino··on Git 3.0's upcoming SHA-256 default will be a costly mistake
I don't particularly like the terminology but, still, the attitude that TLS is just for "sensitive" websites, actions, or data is quite wrong. Even if you don't care about surveillance or ISP ad injection, you should care about infrastructure compromises and exploit injection. It sounds exotic and high effort but it's not. Unless you're personally auditing the coffee shop, airport, library, hotel, etc. Wi-Fi hardware and network before you connect, it could affect you easily. It's not even a choice that reasonably belongs in the hands of website owners, because they don't control all the paths from users back to them. And even residential and municipal ISP networks can get compromised, too, along with data centers and everything in between.
kbolino··on When oil prices spike, where does the money go?
> Do farmers triple or 5x the price of food during droughts?

Yes, actually. The price of 1 pint of real vanilla extract is currently $9.59 at Costco. I have seen it as high as $42.99. That is a nearly 5x spread, and it largely depends on the weather and politics in Madagascar.

kbolino··on Platform-independent SIMD in Go
The exact equivalences between the two languages are not the point, and anyway, Go arrays are fixed-size and have no Java equivalent.

The point is that Java does not have this problem in the language or the standard library. Of course, you should not write racy Go code; the language provides ample ways to avoid the race, such as channels; and the race detector will generally find such racy code, provided you turn it on. However, the issue is that this race leads to memory-safety violations; it can occur especially in code written by novice Go programmers, and it's well acknowledged by the language authors [1].

[1]: https://research.swtch.com/gorace

kbolino··on Platform-independent SIMD in Go
Java arrays are thin pointers to Array objects, which contain the length and data in the same place (on the far side of the pointer). Since thin pointers cannot tear and Array objects cannot be resized, Arrays themselves are always memory-safe in safe code.

Go slices are fat pointers to undecorated memory. The slice itself is a 3-tuple of pointer, length, and capacity. If you append to a slice that's already at capacity, the Go runtime will allocate new memory for you and return a new 3-tuple. If you assign that result to a variable that's also being accessed by another goroutine, the latter can observe the slice in an inconsistent state. It can, for example, see the old pointer but with the new length, allowing out-of-bounds access. None of this requires unsafe code.

The same issue applies to string and interface variables, which are also fat pointers.

kbolino··on Rust is tier-1 language at Microsoft
It's doable, but more difficult. Also, digital distribution platforms will overwrite your modified binaries with abandon, considering them to be corrupt. Of course, if mods are supported anyway, they could be supported through dynamic loading, but that's still inferior to the power afforded by e.g. Harmony and a good JIT compiler.
kbolino··on Rust is tier-1 language at Microsoft
I've never downloaded a C# mod from a Discord server. They are typically available via first-party sources (e.g. CoI Hub for Captain of Industry), reputable* third-party sources (e.g., Nexus Mods, Thunderstore for Valheim), or even the same distribution platform as the game itself (e.g., Steam Workshop). While developer support is all over the place, it usually amounts to putting a ZIP file in a specific directory.

Bethesda is not a very good counter-example, in my opinion, because they regularly** break mod support. I don't know enough about how these mods work to be sure, but given that the 32-bit to 64-bit transition was one such major breaking event, I am suspicious that they are not actually effectively sandboxed. I also don't play many Paradox games (and the one I did play a lot of, Cities Skylines, is a special case, and also a Unity/C# game), so I don't know how they do things.

The best example I know of for strong, sandboxed, first-party mod support would be Factorio and its Lua mods. However, these mods are constrained to doing only those things the game developers anticipate and want to allow, which means every mod for Factorio produces a very Factorio-flavored experience, though AFAIK there are some extension points in the API which are only really used by mods (or were, at least, before the Space Age expansion).

The biggest downside of C# (and Java) mods is indeed security, but the biggest upside is that a lot of games can add modding support without too much extra work. Such extra work is often not possible (they don't own the engine) or unlikely to happen (they don't have the time/budget), outside of special cases.

* = Whatever else you might think of Nexus Mods and its policies and drama, they generally take malware seriously

** = Roughly every 3-4 years, which means it's happened like 4 times with Skyrim, and one of those was so major that it completely split the mod ecosystem

kbolino··on Rust is tier-1 language at Microsoft
The CLR is the reason most C# games are moddable.
kbolino··on Turns are Better than Radians (2022)
> I miss strict, deterministic unsigned addition overflow. In many modern languages, all kinds of verbose hoops are required to get this behavior and there's a chance it will generate terrible machine code.

Most modern (post-2000) languages actually handle this perfectly fine. They have both signed and unsigned types, with exact bit widths and fully specified semantics, which generally match what the hardware is natively capable of, at least on modern processors.

It's languages from the 1990s that make this complicated. There must have been something in the water that motivated language designers in that decade to "simplify" the number system. Maybe this was an overcorrection from even older languages, which generally weren't trying to be clever but were trying to be portable, at a time when a lot of the basics hadn't been nailed down yet, like 8-bit bytes and two's complement.

kbolino··on Go 1.27
Repeating the package name is fine if it's exactly the same name (modulo capitalization) and there's nothing better to name the type anyway. The style issue would arise with e.g. uuid.UUIDVersion, which should just be named uuid.Version. There used to be a gopls lint that would flag names like uuid.UUID but it got relaxed awhile ago.
kbolino··on How Go detects struct copies with sync.noCopy
Sure, you can just ignore errors entirely, and this is what Rust actually does today, at least for std::fs::File. The only other option within RAII that I can think of is that you can mutate some external, longer-lived state.

The primary way to deal with error-on-clean-up in RAII languages is to not rely exclusively on RAII for it. Rust's File type, for example, has sync_data and sync_all methods (which, to be fair, only even need to be called for writable file handles). I don't think there's anything wrong with this approach, but it ends up being just as explicit and therefore forgettable as defer.

It should be noted that you can (at least in Rust) actually implement defer using RAII; see e.g. the scopeguard crate. Since RAII is block-scoped, this defer is also block-scoped (like Zig) rather than function-scoped (like Go).

kbolino··on How Go detects struct copies with sync.noCopy
RAII has the advantage that you can't forget to do it, but defer has the advantage that you can handle failure in ways other than panicking. Of course, in many cases (e.g. closing a file), there's generally not much you can do anyway even if you want to handle the error directly, but at least it's possible.
kbolino··on How Go detects struct copies with sync.noCopy
You can exploit the mechanism described in the article yourself, but it's already changed once in the past and is not part of any compatibility guarantee. As with structs.HostLayout, a blessed structs.NoCopy in the standard library could guarantee that it works forever. I think the bigger issue remains that it doesn't actually do anything in the language (but, then again, neither does structs.HostLayout--yet).
kbolino··on How Go detects struct copies with sync.noCopy
Interfaces in Go (the closest equivalent to traits in Rust) don't have to have methods either, but they are structurally typed. This is like "static duck typing" if you will. So an interface with no methods is implemented by every type in the language (indeed, this became such a useful pattern that the name "any" was reserved for it in Go 1.18).

Marker interfaces can exist in Go, but here they are another kind of hack. They must define at least one method (which doesn't have to be public), though that method is never actually meant to be called.

kbolino··on How Go detects struct copies with sync.noCopy
This feels like a style complaint and not one about substance. An inaccessible (because it's named _) zero-length struct field is just another kind of metadata. It also doesn't require you to pollute the method set, which would be a bigger issue.

The real hack to me is that anything which simply has Lock() and Unlock() methods is considered uncopyable.

kbolino··on Xorshift Generators
Whether your recommendation is valid seems to be quite CPU-dependent. Cf:

  cpu: AMD Ryzen 5 5600X 6-Core Processor             
  BenchmarkAES_CBC-12             100000000               10.96 ns/op
  BenchmarkAES_CTR-12             83161234                14.36 ns/op
  BenchmarkPCG-12                 345336063                3.463 ns/op
  BenchmarkChaCha8-12             174143492                6.894 ns/op
  BenchmarkXoshiro256p-12         254343658                4.717 ns/op
  BenchmarkXoshiro256pp-12        266837442                4.496 ns/op
vs.

  cpu: Apple M4 Pro
  BenchmarkAES_CBC-14             162698020                7.370 ns/op
  BenchmarkAES_CTR-14             242501074                4.954 ns/op
  BenchmarkPCG-14                 197000988                6.083 ns/op
  BenchmarkChaCha8-14             237430095                5.050 ns/op
  BenchmarkXoshiro256p-14         252911710                4.738 ns/op
  BenchmarkXoshiro256pp-14        252656401                4.745 ns/op
Code: https://gist.github.com/kbolino/afbb86f3c9b2bd2f87272801d156...
kbolino··on Rust SIMD on the GPU
Go doesn't have auto-vectorization in the first place, so its portable simd library is at least partly there to fill the gap.
kbolino··on TSON – A JSON superset with immutable, hash-pinned schemas
I did a cursory look through a number of major languages and libraries*, and as far as I can tell, they all retain and provide access to the fragment by default. Maybe some of them didn't in the past, or the APIs that I found superseded older ones that didn't, but at least in the present day, this doesn't seem to be an issue.

* = Including urllib.parse (Python), url_parse (PHP), java.net.URI (Java), System.Uri (.NET), net/url.URL (Go), curl_url_get (libcurl), URL (JavaScript, which calls it the "hash"), url::Url (Rust), Boost.URL (C++)

kbolino··on TSON – A JSON superset with immutable, hash-pinned schemas
Yes, this is just how fragments work; they're supposed to be stripped before sending the request to the server. This is exactly why, in the context of the thread, the fragment is the correct place to put the TSON hash parameter.
kbolino··on TSON – A JSON superset with immutable, hash-pinned schemas
Using the query string to carry the sha256 hash but then saying the "hash parameter is verification metadata, not identity" doesn't make much sense. The ?query part of a URL is supposed to be sent to the server. If you want to add client-side (meta)data, you should use the #fragment part of a URL. See RFC 3986, sections 3.4 and 3.5: https://datatracker.ietf.org/doc/html/rfc3986#section-3.4
kbolino··on Go 1.27 Interactive Tour
Rust's solution (the ? operator) is actually applied to the Result type (and, IIRC, Option and ControlFlow). Go does not have a Result type. This makes a 1:1 port of Rust's solution impossible.

Moreover, the "obvious" solution, i.e. treating (T,error) returns the same as Result<T,E>, does not actually work. The problem is that (T,error) behaves like a product type while Result<T,E> is a sum type and yes, some Go code does (ab)use this. In particular, the io.Reader.Read method in the standard library allows implementers to return (n>0,io.EOF), which has no equivalent Result representation. Many people consider this allowance to be a mistake, but it's too late to change it.

kbolino··on Go 1.27 Interactive Tour
You need this in Java because interfaces are explicitly implemented. You don't need this in Go because interfaces are structurally implemented. The way you spell "anything that has a method named Size which returns int" is interface{Size() int}.
Page 1 of 34Next →