Hacker Newsnew | past | comments | ask | show | jobs | submit | treyd's commentslogin

> the very thing you reach for a low-level language for - they typically require unsafe

There's a formal proof asserting that if you keep up the safety invariants within an unsafe region then that will not infect other code, even in the presence of arbitrary other correctly-written unsafe blocks.

This means you can build abstractions on top of these low-level primitives to keep it contained, so consumer code never has to even think about or know there's unsafe blocks in it. The type system lets you build very powerful abstractions so these go a long way.

There's a lot of woo-woo scare quoting around how much you actually have to use unsafe code in Rust. It's fairly uncommon to actually have to reach for them in practice. Most of my usage ends up being things like converting a &[u8] to a &str when I know it's already valid UTF-8 so I want to skip the linear-time validity check. Very rarely do I have to build data structures with complicated pointer juggling, because there's often a library that already does what I need!

> which is that over time, as program changes and evolves over years, things tend to drift toward the more general mechanisms that rely on malloc/free on an individual objects, and the program gets slower and slower

What are you talking about? I've never encountered this and I've been using Rust for 10 years.


> > which is that over time, as program changes and evolves over years, things tend to drift toward the more general mechanisms that rely on malloc/free on an individual objects, and the program gets slower and slower

I think the idea is that a small program can organize its allocations and data structures to minimize number of calls to malloc, e.g. with preallocated workspace structs, or slab allocation, and similar approaches. But as a program gets bigger, there's a pressure to have looser coupling, to have subsystems with simple convenient APIs which leads to them doing on-demand malloc calls internally, rather than having consumers pre-allocate their needed workspace. Because that kind of workspace management results in more complex APIs and more burden on the consumer.

That said, I don't really believe it either, at least for the kind of codebase where it would matter (scientific computing, in-memory DB server, etc). A codebase that places an emphasis on minimizing heap operations in hot codepaths can do so by consistently using workspaces and allocation-avoiding APIs. I don't think it's so difficult really, but it does take a conscious design decision to do so. But writing something like a web browser in this way could be annoying due to most data having wildly variable sizes, and zig's arena concept would be very handy -- but rust has crates like bumpalo for that purpose.

My personal mantra: "Think in FORTRAN, code in Rust/Julia/C++". But I'm mostly working on HPC-style code where I don't have to do with wildly varying input or output sizes.


> but rust has crates like bumpalo for that purpose.

Except that's not composable - not only do you need specialised data structures, but all (transitively) allocating calls need to be specialised. That's the exact same issue we have in C++, and that's the issue Zig seeks to address. BTW, just the other day there was a post here about a language with another interesting approach, but I have yet to give it a close look: https://github.com/aardappel/goose/

> But I'm mostly working on HPC-style code where I don't have to do with wildly varying input or output sizes.

There you have it. The problems arise more quickly in concurrent rather than parallel code, and when there are lots of features added over the years that touch the hot paths.

> in-memory DB server

Actually, here there can be big problems (as it's also about concurrency rather than parallelism). Last week a colleague of mine looked at Moka and saw that it could only offer half the throughput as Java's Caffeine at the same latency and RAM footprint (almost; the Java program used 5% more RAM). When he looked into it, he saw that over 40% of the program's CPU was spent on the epoch-based reclamation.


> not only do you need specialised data structures, but all (transitively) allocating calls need to be specialised

Most crates for containers will be written such that the container types take an optional allocator type parameter that defaults to the global allocator. You can set it and it transparently uses the other allocator.

To improve the ergonomics, you'd define local aliases that use that allocator.

    type MyVec<T> = Vec<T, A = MyAlloc>;

When that is the case (and it isn't yet; and remember that it's not only the containers, and strings, that need to be parameterised, but any routine that allocates them, transitively), then that's what Zig does. But the question was doesn't Rust solve memory management already, and this is an important aspect it clearly doesn't solve just yet.

i find it fascinating how big of a rust hater you are. willing to outright lie to make your point

It would be helpful if you named the falsehood for those of us following along.

because you can do this

   with_allocator(&arena, || {
      third_party_library::do_work()
   });
there is nothing stopping you from using custom allocators with your own code or with calls to thirdparty dependencies

but custom allocators are rarely used in rust because they're simply not needed the vast majority of the time. if your language is not memory safe and you need to manage memory yourself, they're more important. but this isn't the case with rust.

c and zig folks are obsessed with arena allocators particularly because they can group lifetimes of individual objects, reducing the amount of malloc/free calls and thus the amount of use after free, double free, nullptr derefs, or leaks that can occur.

in rust this isn't a concern so custom allocators are only used for performance reasons.

but it turns out that in performance sensitive areas, you generally use custom data structures or those that already have their own allocation strategy baked in, like the generational_arena crate.

most of the time you are not calling thirdparty crates that allocate in performance-sensitive regions. either the crate is designed for this usecase and already uses a performant allocation strategy, or you're writing your own code here.

and in the rare case, you can trivially vendor the crate and pass your own allocator into it, or toggle the global allocator for callers.

but you also need to benchmark first before choosing an allocation strategy because it's not clear that a custom allocator will always guarantee better performance anyways.

and btw zig doesn't guarantee this anyways. you could pull in a dependency that instantiates their own allocator. at least in rust almost all crates use the global allocator as a default which lets you swap it out. if a zig dependency uses their own allocator the only recourse is to fork it.

rust doesn't have a performance problem, so any claims about it's custom allocator support leading to poor performance is unfounded. and thus so are claims about the superiority of zig's approach to allocators.


> i find it fascinating how big of a rust hater you are. willing to outright lie to make your point.. because you can do this with_allocator

You say I outright lie for not mentioning the existence of something that doesn't exist??? I guess you're saying it's possible to create such a mechanism (or that some libraries do create ad-hoc ones), but that's not the point.

> there is nothing stopping you from using custom allocators with your own code or with calls to thirdparty dependencies

I didn't say there's anything in the language stopping C++ and Rust from having such a standard library and ecosystem of libraries. They just don't have that yet.

> if your language is not memory safe and you need to manage memory yourself, they're more important. but this isn't the case with rust. c and zig folks are obsessed with arena allocators particularly because they can group lifetimes of individual objects, reducing the amount of malloc/free calls and thus the amount of use after free, double free, nullptr derefs, or leaks that can occur. n rust this isn't a concern so custom allocators are only used for performance reasons.

This is simply untrue. I won't call it an outright lie, as it's probably just a lack of experience with low-level programming.

First, I'm trying to point out the problems we've had in C++, most of which only became apparent when evolving large codebases over time. People who have not had experience evolving large C++ or Rust codebases over years simply don't know about these problems and certainly can't claim they don't exist. Writing smaller programs in C++ or even large but young programs has always been a pleasure. The language is expressive and productive. Some of the biggest issues only arise years later, when the program gets either expensive to maintain or slow.

Second, experienced C and C++ folks cannot be "obsessed" with arenas for the reasons you mentioned because until maybe 20 or even 15 years ago memory safety wasn't a widespread obsession. It was a correctness issue like all others, and its outsized role as the cause of security vulnerabilities wasn't widely known until more recently.

Lastly, you don't pick Rust for safety. Most software in the world today is already written in languages that are at least as memory-safe safe as Rust, sometimes more so. These days, you pick C, or C++, or Rust, or Zig when you want to do something that's largely low-level. Things that are low-level often also need to be reasonably fast, and large low-level codebases that evolve over years tend to suffer serious performance issues because of memory management (because, being low-level, they can't move pointers and so can't use things like a moving GC to reduce the overheads of their malloc/free runtimes; this is why companies with actual experience with long-maintained large low-level codebases make huge runtimes like TCMalloc to help them to a degree, which you also may not have needed yet), and arenas are the primary way to get memory performance similar to what you see with modern moving GCs (and even somewhat better).

Now, you could say that C++ only started moving in that direction with pmr in C++ 17, and that's true. But the need was recognised as early as 2005, traditionally C++ codebases didn't rely on many libraries so interoperability has typically not been a large concern, and the number of large C++ programs that would benefit from such a thing declined over the years because of the low-level maintenance issues I mentioned and the growing availability of fast high-level languages.

My distaste for Rust isn't because I like C++ so much. Even though it's been one of my primary programming languages for the past 25 years, I "hate" it for the very same reasons. Most Rust superfans are people who have not had enough experience with it and they don't know about the problems. Not all, of course, and even C++ has superfans, which is why I said that among the people who are experienced in low-level programming, there are people who like the C++/Rust approach (of trying to make low-level code appear high-level) and people who don't.


I works in pretty low level OS code. I promise you most of our code would be unsafe. And using unsafe in rust is less ergonomic then using zig or c++.

We could use rust. But it wouldn’t give us anything.


This hasn’t been the finding of the R4L project. Go look at their code, it’s shockingly safe outside of the parts that interact with extern “C” symbols, which naturally need to be unsafe.

> There's a formal proof asserting that if you keep up the safety invariants within an unsafe region then that will not infect other code, even in the presence of arbitrary other correctly-written unsafe blocks.

In general "unsafe" does not compose.

"if you keep up the safety invariants within an unsafe region"

This condition is doing a lot of heavy lifting.


Here's an article about the research on it which lays out the properties in simple terms: https://smallcultfollowing.com/babysteps/blog/2016/10/02/obs...

I'm curious why you think that statement is doing heavy lifting. It's much easier to write and verify that a few lines of code are correct than it is to write and verify that an entire program is correct. But that's the norm in C and Zig, and historically people haven't been very good at it. That's why we try to do it as little as possible.


Many more C programs have been verified than Rust programs. Also, Zig's spatial and memory safety is as good as Rust's, so it's not really similar to C at all.

The reason it's not "the norm" is that (especially with spatial safety taken care of), not every line is equally dangerous at all. Still, there's no doubt that more guarantees help, but that is only when all other things are equal. If you pick a low-level language for mostly low-level things, so Rust doesn't offer safety for the trickiest code, and furthermore it makes certain things harder to see because the language is more complicated, then things become much less clear. Obviously, when the vast majority of the trickiest, most important code doesn't need to be low-level, Rust would probably be safer on the whole, but in such situations I see no reason to choose either Rust or Zig. You need to choose a low-level language if the core of what you're doing needs to be low-level.


> Also, Zig's spatial and memory safety is as good as Rust's

Is there a word missing before "memory"? Seems odd to specifically call out spatial memory safety when memory safety subsumes it.


That's because Zig offers spatial memory safety (e.g. buffer overflows and index out of bounds), but no temporal memory safety (e.g. use-after-free). I suppose the "and" before "memory safety" is a typo.

sorry, the "and" was a typo

Usually people say “spatial” vs “temporal”.

https://internals.rust-lang.org/t/language-vision-regarding-...

You must reason about the invariants in unsafe code on a global level. In particular, you could have unsafe code in crate A, whose data are then used by crate B. It could be fine. But then crate B changes its implementation which now violates the invariant expectations of crate A.


> In particular, you could have unsafe code in crate A, whose data are then used by crate B.

Is this backwards? If B consumes data from A then to me that does not imply that A depends on anything from B; for a more concrete example that sentence reads to me like A is basically "throwing data over the wall" to B and whatever B does with said data is of no relevance to A. As a result, if B changes that shouldn't affect A.

Also for what it's worth I get the impression you and treyd might be talking about slightly different things when talking about whether unsafe code composes. I believe treyd is referring to the RustBelt series of papers [0, 1], for which the statement "unsafe code composes" means (at a high level) that adding a module with a memory-safe API to a memory-safe system will result in a memory-safe system as long as the implementation upholds the safe semantics. Yes, the last bit can be a rather significant caveat, as you said.

What you're talking about seems more along the lines of needing to look beyond the boundaries of unsafe blocks to prove that the unsafe block upholds its invariants, which is also true. I think you only need to check within whatever safe encapsulation boundary is relevant, though, rather than globally.

[0]: https://people.mpi-sws.org/~dreyer/papers/rustbelt/paper.pdf

[1]: https://plv.mpi-sws.org/rustbelt/rbrlx/paper.pdf


> Is this backwards? If B consumes data from A then to me that does not imply that A depends on anything from B; for a more concrete example that sentence reads to me like A is basically "throwing data over the wall" to B and whatever B does with said data is of no relevance to A. As a result, if B changes that shouldn't affect A.

This is a specifically crafted bad idea, but you could have module A use unsafe to craft a Vec<u8> that is safe to use to read or write, but not to grow or shrink. You declare an invariant that the receiver shalt not grow or shrink the Vec.

If B only reads and write, you're good. But if a future B breaks the invariant, bad things happen. As I said, specifically a bad idea; there's a much better type to use if the thing can't grow or shrink...

No real world example, because I don't think we've run into memory safety issues with unsafe in the Rust code base I work in... but we only use unsafe where it's required (syscalls and other FFI).


Hrm, I had assumed that A was providing a safe API, in which case I think A would be considered "at fault".

Sure, A is at fault, but it only broke when B changed behavior.

This is true. In Java, we have a notion we call "integrity", which is a generalisation of memory safety and includes a host of properties guaranteed by the platform. It includes memory safety, but also things like "a non-public method cannot be called or a non-public field cannot be accessed (even reflectively) by code in another module".

To address the problem that once integrity can be violated anywhere, only global analysis can prove that nothing bad happens, we've done two things:

1. We require the application to explicitly permit any integrity violation by a module; i.e. a library can't allow itself to violate integrity. This is a principle we call "Integrity by Default" (https://openjdk.org/jeps/8305968).

2. We try to minimise the need for potential integrity violations (this is very different from Rust, which requires unsafe even for things like benign write/write races, which are fairly common, and various basic data structures). Over the years we've offered safe replacements for things that used to require Unsafe. In other words, clearly demarcating unsafe code isn't enough if it's needed at all in many situations.

It isn't perfect, of course, as some libraries do require unsafe operations for direct interaction with native code or with memory, but their number has been greatly reduced, and they cannot do this without the application's explicit approval. Interestingly, this has annoyed library authors who want to do unsafe things but don't want to application authors to be alarmed because "we know what we're doing," and it's also annoyed some application authors who want to use such libraries and are forced to explicitly add permissions. But I think that the community, as a whole, has eventually accepted this because the harm done to those who don't care is small (they just need to add the permissions), to those who do care it helps a lot, and because fewer and fewer libraries require "integrity-busting" permissions, many applications need to do absolutely nothing and get important guarantees for free.


> this is very different from Rust, which requires unsafe even for things like benign write/write races, which are fairly common, and various basic data structures

I know this paper [0] is quite old at this point, but the mention of benign data races reminded me of it. Would you happen to know how applicable it is to modern memory models?

[0]: https://www.usenix.org/legacy/event/hotpar11/tech/final_file...


Benign write/write races (when multiple threads do unordered writes of the same value to the same address) are quite common and useful, both in parallel algorithms and in lazy initialisation. Useful benign read/write races are far more rare to the point I'd say it's ok to assume they don't (or shouldn't) exist.

However, in C and C++ (and Rust) benign non-atomic write/write races are UB (indeed, LLVM also treats them as potential causes of UB). In C# and in Java they are safe (although Java currently only has non-atomic writes on 32-bit machines, but soon they'll be more common when value types are enhanced). LLVM even has a specific construct to support the Java-style memory model (https://llvm.org/docs/Atomics.html#unordered), and Zig lets you use it (https://ziglang.org/documentation/master/#atomicStore).


That's always been true in all the safe languages with unsafe escape hatches, except here these "primitives" are the main reason to reach for a low-level language in the first place - because they presumably require the control that low-level languages offer. Combining them in the same language might appeal to some and not to others who think that the high-level, safe parts are unnecessarily complicated because it needs to integrate with the low-level parts, and the low-level parts are unnecessarily complicated because they need to integrate with the safe parts. Anyway, some like this and some don't, but my point is that it's not "mostly solved".

> What are you talking about? I've never encountered this and I've been using Rust for 10 years.

Okay, but I've been doing low-level programming professionally for 25 years, and have encountered this over and over in large programs (over 500KLOC) as they evolve.


That's just not an accurate description of how you write Rust in practice. There's no separate "high level" and "low level" parts/forms of the language any more than the software development process already is all about building abstractions. You should be doing this in Zig, too.

It's just that sometimes some of the abstractions you need to build go outside what the ownership and borrowing system can model. And when you don't need to do that (which is 99% of the time) you also get all the benefits of the ownership/borrow system for free.


They're not separate forms but they are separate modes, and it is precisely because the language tries to fit both these modes into the same language that both suffer. I fully understand the goal of trying to unify these modes into the same language (C++ does the same thing), but there have always been very experienced people who like this approach and those who dislike it, hence it's not "solved". Something is solved when there's a broad consensus it's solved, and there isn't one here.

I mean, someone can think it's solved for them, but if they're asking why others don't see it the same way and why many expert low-level programmers are at least intrigued by Zig, this is why. I prefer a simpler high-performance high-level language for high-level things, and a simpler low-level language for low-level things, and I dislike the C++/Rust approach of combining them into one complicated language. Some may think you get the best of both worlds; others, like me, think you get the worst of both worlds.


Can you point to a specific example that ends up being a "worst of both worlds" in your perspective?

I don't know exactly how specific you want to be, but sure, because we've come across this countless times in C++, which suffers from the exact same problem.

Suppose you're writing a program that's mostly high-level, say some kind of concurrent server, and it's large-ish, say around 1MLOC (most C++ programs I've worked on were significantly larger). Because the language is also a low-level language, it has low-level constraints, so:

1. It needs to use an AOT compiler, and consequently to get good performance you need to use less general mechanisms, such as direct (as opposed to dynamic) dispatch and even manual monorphisation (with generics/templates). These are viral, so they have to be carefully chosen (you can't monomorphise everything or you'll get machine code explosion). Five years later you need to make a big change that requires more generality, and then you either have to reconsider all of your manual optimisations, which is expensive, or go for more general constructs (dynamic dispatch) and the program gets slower.

2. It needs to use machine pointers (i.e. you can't enjoy a moving GC), and so you try to use the stack as much as possible (which you can't really do for anything dynamic), or suffer the high cost of malloc/free on individual objects. As the program evolves, you need to make things more general, and objects that could live on the stack now need to go on the heap, and objects that lived on the heap now may need to be shared among threads, in which case you often add the additional cost of refcounting GC. Of course, you want to use arenas in many cases, but they're very, very hard to use in C++ and Rust.

You'd be better off - performance-wise and maintenance-wise - with a good optimising JIT and a moving GC. This was exactly a problem with many C++ programs that didn't really need a lot of direct hardware interaction - everything worked great for a few years, and then the evolution and maintenance costs became really high (or the programs became slow).

Now suppose you're writing something low-level, i.e. you really need to interact with the hardware and/or OS directly a lot, and want to control everything - where everything is in memory, exactly when it's initialised, exactly when it's freed, exactly which operations are executed and when. But now you have a language that's also high-level, so it has a lot of implicitness that hides from you the things you want to see (and in Rust's case, you lose the safety). Best case scenario, you rely on disciplne and avoid implicit features, but then you also need to avoid much of the standard library.

Anyway, combining high and low level in the same language was C++'s dream: one language for everything. Of course, for a while we didn't know about the maintenance problems, as those appear only years down the line, but more importantly, there weren't really high-performance high-level languages back then. These days, with lessons learnt and with more options, I prefer a language that focuses on being high-level for high-level stuff, and a language that focuses on low-level for low-level stuff. If you really need both kinds, use two languages.


> I've been doing low-level programming professionally for 25 years

You haven't been doing any Rust though. You seem to think you can extrapolate your C++ experience to Rust. That's preposterous. The actual Rust programmers can't recognize this theoretical problem in their Rust programs.


It's not theoretical, it's one of the main reasons many large applications abandoned C++, and there's absolutely no reason for it to not exist in Rust. All low-level languages suffer from expensive evolution for fundamental reasons - the reliance on an AOT compiler and the lack of movable pointers impose serious performance tradeoffs in large programs. Optimising JITs and moving GCs were invented, in large part, to address this very real problem, familiar to many low-level programmers who have maintained large codebases for a long time. It's also why large runtimes like TCMalloc were invented to assist as much as they can.

Most actual Rust programmers haven't maintained a large Rust program for a long time. Now, don't get me wrong - there are many C++ programmers who are fine with it, but many who aren't. What I find annoying is people without much experience in Rust assuming that everyone or almost everyone should like it, even though that's never been true for any language. I'm not saying Rust is bad by any means; in fact, I think it's better than C++ in a few ways. I'm explaining why I don't like it.


Could that be because the language is fairly young? You don't see the "20-year-old legacy system" in Rust because it doesn't exist yet ;)

And if you look at other comments in this thread, many engineers have this mentality of "just use a crate, it's probably optimised already". They might not have performance problems immediately or obviously but it's more like ten thousand papercuts - a few allocations here and there, a few extra copies here and there and you've got a way slower program than it should have been.


Well, the problems don't start after 20 years but after 5 or so (depending on the size of the codebase and the rate of the application's evolution), and the reason there aren't many large and oldish Rust codebases isn't because the language is too young for that (work on it began twenty years ago, and it's been stable for over a decade); that's middle-aged for a programming language. When C++ was of a similar age, there were thousands of >1MLOC programs written in it. One reason is obviously because when C++ was of the same age, there weren't as many suitable high-level alternatives, and people just don't pick a low-level language for most large applications anymore. But most Rust fans at least on social media, have not actually had much experience with it or with low-level programming in general; I'm guessing most haven't worked on Rust projects with more than 10 full-time people on them (this isn't normal in the industry, BTW, as a lot of software lives in large programs). And again, there are people who can certainly live with these issues, but they are real, and many certainly find them troubling.

A system that makes "business decisions" indistinguishable from malice is an interesting one.

If I have something you want and I could give it to you but instead charge you money, is that malice? If so, what system wouldn't make malice indistinguishable from business decisions? If not, what distinguishes that from a login wall in exchange for viewing content?

What are the architectural differences between this and Android's ahead-of-time runtime?

This JEP is essentially about caching the JIT's generated code for later runs. The JIT is still free to discard the cache if it deems it worthwhile.

I don't understand why few people are pointing out the obvious vulnerability here that you can control the wires going into the photosensor controller and pretend that the photosensor is capturing whatever image you want. I imagine it's not exactly trivial to do this, but a grad student with an FPGA could probably figure it out.

>a screen attack still works: photograph a screen displaying an AI image and you get a signed photo of a fake

you don't need to do that just photograph a screen.

This seems close to worthless in "identifying real photos vs AI" for someone actually wanting to do something bad with an AI image, although probably very useful at identifying which phone took a photo when ("the root of trust stays inside Apple's Private Cloud Compute") seen as it's not an entirely local solution a bad actor government could use their powers to completely abuse this.


its conflicting desiderata: a videographer doesn't want to constantly power a device to maintain provable continuity, but screen attacks necessitate such a scheme

a continuous stream of video from factory to customer to observation should prevent screen attacks, if there is a trustworthy framework for processing and checking the absence of screen slide-ins etc.


if geolocation data can be captured in the same signature, that would be a good enough approximation for most relevant cases I think.

GNSS signals can be relatively easily faked because the original signals are very weak so overpowering them doesn't require much broadcast power.

Jamming them is easy, replaying them so as to trick unacquainted receivers is easy, but "faking" a network of signals so as to precisely control present a specific location is not easy or feasible.

"Overpowering" (as to jam) inherently means detectable, these signals are arriving below the noise floor anyway. And if you aren't overpowering, the original signals will leak through. Also, depending on the sophistication of the receiver, your ability to present an implausibly different location may not exist at all (AGPS.)


It seems feasible for state actors, at least: https://en.wikipedia.org/wiki/GNSS_spoofing#Ocurrences

Yes, a motivated actor can move a naive receiver somewhere different than where it thinks it is by some small amount - ideally outside of the CEP of whatever weapons system is targeting, but that's much different than precisely controlling the location to be somewhere else arbitrarily.

ah, damn.

Because doing so does not materially devalue Apple’s product. Sure, a dedicated attacker could try to overcome it, but few will, and only people of such serious consequence that they can afford the effort of modification. By and large this puts Apple into direct competition with Nikon and it’s long overdue that someone ship this capability to a wider market than authorities.

Also, remember how Touch ID sensors are cryptographically paired, and consider whether Apple could bake that into a camera sensor rather than a fingerprint sensor. If they can, then you can run wires all you want; the attestation chain will not be valid. I’d be shocked if they were willing to launch the product without that, and there’s a new hardware dependency or else they’d have released it for earlier phones.


Simpler than that, you can just talk to the cryptography IC yourself and ask it to sign stuff. No need for an FPGA, just an arduino. Given the datasheet I imagine any LLM from the last year should be able to oneshot it.

if

I imagine on apple silicon this is buried deep in silicon / ISP IP block, and isn't a discrete IC.


But it is, the discrete IC is pictured in the article.

I think you're talking past each other.

You're talking about the device that's in the blog post; the person you're replying to is talking about the thing that Apple is shipping soon.


Or, as the author said, you can just photograph an AI generated picture, and that will work too.

This feature is Pro phones only, not Duo: https://www.apple.com/iphone/compare/ ("Apple Reference Image (Fusion Main)")

So only on devices with LiDAR / that can capture depth map.


If there is signed metadata too, then it's pretty hard. You will need to match focus distance (it will be very small if photographing picture), GPS location, exposure and other settings. If there is a depth map, you'll need to match it too.

Adding depth sensor info to the this could help

Even easier is to just take a picture of an AI-generated picture.

It's actually worse if it is plausibly trustworthy for "99.9%", since that's enough that naive users will get accustomed to believing the verification badge is authentic.

When a motivated malicious user (who doesn't actually need that much resources) will be able to convince people something is authentic because the verification passes when it shouldn't since naive users are primed to believe it by default.


Read the rest of my comment please. Is the single motivated malicious user able to do as much damage as all of the blocked attempts put together? Probably not, since if there's really all that much riding on it, people will point out it can be bypassed.

Should we also abolish Pangram, because it's not 100% accurate? Someone might be convinced a text is not AI-generated when it actually is! We should get rid of it rather than fool people into thinking it can be determined accurately. What about antivirus? We should abolish it as well rather than fool people into thinking that their software is ever 100% safe. What about HTTPS? We shouldn't call it "secure" shell because the computer you're connecting to could be compromised! I could go on and on and on.

The median instance of AI image generation isn't evidence in a court case. It's cyberbullying, or deepfakes, or fake news. It's called "slop" because there's a lot of it being churned out at low effort.


> Is the single motivated malicious user able to do as much damage as all of the blocked attempts put together?

Yes, absolutely. Probably moreso. The whole point of these proposals is to try to solve for the "motivated malicious user" who is engaging in actual high-stakes fraud. There is no point in applying techniques that suppress inconsequential pranks while making serious crimes easier to get away with.

This really seems like a rehash of the perennial DRM argument: DRM restrictions provably do not reduce large-scale motivated copyright infringement, they just annoy legitimate paying users. This is the same class of solution, in that it is effective only where the stakes are low and the impact is minimal.


Why is this being framed as 2 types of users, lovable pranksters and fraudsters? There's a whole spectrum between these 2.

Also I'd like to know if a "joke" is likely fake.


I don't suspect there is a uniform spectrum between those two. I think this is something that's going to be clinal, which we see in a lot of other comparable social contexts. The number of people actually willing to cross a moral threshold into outright crime is relatively small, but those are precisely the people who cause the most damage when they get away with their behavior.

Bur I don't even really think that's really relevant anyway, because whatever the density of "malicious" motivations is, the point here is that the fact that it only is an effort/motivation threshold that allows this technique to "block" malicious uses, and the motivation to overcome that threshold correlates directly with the stakes involved in the malicious use.

In other words, the more malicious the abuse is, the less effective this solution will be: the boundary of its usefulness will be wherever the line between pranksters and actual criminals happens to lie.


Your idea of a criminal seems to be hypercompetent and think of everything. These do exist, but most criminals are not very smart. Smart, dedicated, technical people can typically make more money legally.

Your argument applies to any imperfect security technology -- aka practically all of them.


> Your idea of a criminal seems to be hypercompetent and think of everything.

No, my idea of a criminal is someone who is motivated to commit crime, and I feel that we've already established in this thread that the approaches we're discussing are motivation gates far more than competence gates.

> Smart, dedicated, technical people can typically make more money legally.

Then who's been running all the botnets, writing cryptolocker malware, and running phishing scams for the past couple of decades?

We've always had script kiddies, and now we have people using AI itself to do malicious things. Technical skill has never been an obstacle for sufficiently motivated scammers.

> Your argument applies to any imperfect security technology -- aka practically all of them.

Ultimately, everything has weaknesses, and with enough effort, most measures can be circumvented. But how much effort is enough varies wildly between solutions.

There's a huge gulf between a "no trespassing" sign, on the one hand, and a concrete wall topped with barbed wire, on the other. The "no trespassing" sign only keeps out people willing to obey it; the concrete wall keeps out anyone who isn't willing and able to accept the time, effort, and risk necessary to climb over it or knock it down.

And the point is that using digital signatures to distinguish AI-generated media from hand-made media is much closer to the "no trespassing" side of things than it is to the wall. Maybe it's analogous to a gate with a latch you can open from the other side if you reach over in just the right spot.


Your no trespassing and concrete wall analogy again indicates the black and white thinking here.


I'm afraid it doesn't. To the contrary, treating effective security in terms of how it influences the attacker's cost-benefit tradeoffs, and evaluating proposed measures on whether the effort threshold they create is enough, is quite literally the opposite of black-and-white thinking.

And in this case we can clearly see that a solution that (a) does not substantially increase their costs -- and in fact, as I've pointed out above, only really filters by motivation, not by time, money or effort, and (b) doesn't target their potential benefits at all, is one that isn't likely to be effective.


You're conflating the effectiveness of the mechanism with its systemic impact.

* Pangram: Yes we should really be discouraging people from putting trust in tools like this because they can't be made totally reliable.

* Antivirus: We should be building application environments with robust security models so that malicious software has a limited blast radius (like we do on mobile, like the Linux ecosystem is trying to do with Flatpak, etc).

* HTTPS: HTTPS is a strict upgrade from HTTP so we should be using it everywhere possible. The UI symbols to indicate to users the security expectations they're getting are good practice.

* ssh: This is just an inappropriate comparison.

The HTTPS comparison would make more sense if actually 0.1% of the time when their browser said they were using HTTPS it was just lying.


Pangram: I'm not arguing against encouraging skepticism; I'm arguing that the technology is not useless. It's good for people to know the limitations, but it's still evidence.

Antivirus: "Actually, we should build this hypothetical better thing" is a cop-out.

HTTPS: C2PA is a strict upgrade from unsigned photographs, so it should be used wherever possible.

SSH: Totally appropriate, the entire point of the discussion is whether it's permissible to the user that they might be more secure.


You're missing all of the points that there could be by focussing on random people.

While it is always an individual tragedy when people treat each other badly (e.g. through deepfakes and all), the real threat does not exist on that level.

This is about misinformation and disinformation, so we're talking state actors. And with that, the 99.9% hypothesis does not hold true.


It's funny how people always say something is "a tragedy at the individual level" when they mean "it's not my problem." It's even crazier to dismiss the value of a security feature, just because it might make people feel more secure. That's true of every security feature! Very little of the technology that the web is built on is proof against state actors.

I like being contrarian as much as the next guy, but "Actually, having security is worse for security" is taking it a little too far.


I am repeating myself, but this is about systems, and not about people.

It is however in the interest of the people to keep the systems running in an untainted way.

As said, on the individual level it's a tragedy, but one that can be absorbed somewhat. Democracy itself failing otoh is kinda hard to absorb.

C2PA is not "having security". It is "having an illusion of security for compliance and CYA reasons, that can be fairly trivially exploited by nation state actors". Banality of evil. Again.

___

Actually, come to think of it, "security" is the wrong term there. Signatures don't secure anything. They attest.

Those are different things. Argh and I ran with your term aah


This is pascal's mugging. Democracy itself might fail! You're just inflating the stakes of your hypothetical bad outcome until it can overwhelm any positive upside.

Not to mention, democracy is under just as much or more threat from "banal" fake news created by citizens. People gonna people. They don't need the DPRK lying to them to fool themselves.


Yeah I think any additional security is good. At worst this could help in a lot of court cases. Someone presents photo evidence - it could be manipulated - it could be not. This happens already. Then someone produces an original higher quality version (like a raw photo - which I take even on my phone at all times now) and experts can verify that as the original.

And I agree on state actors. If a major one is invested in something like this they might have well compromised the signing project itself, the verification process, or even the court system or media. That seems like a rare and extremely high bar to guard against.


Exactly. It's just more information. It doesn't have to be 100% accurate in every case, to be useful.


No biological process is 100% precise. DNA copying being imperfect is the driver of evolution, which is the prime example of this. What I assume they're referring to is that their ribosomes just "make fewer mistakes" when creating proteins from mRNA.

These "mistranslated proteins" are normally infrequent and benign, and their material is recycled eventually. But that whole process wastes energy, so making a "better ribosome" makes all cellular processes more energy efficient and allows the energy budget to be reallocated.


The thing with Nostr is that the protocol spec expressly forbids relays from forwarding messages to each other.

What this means is that users trying to reach each other need to shotgun messages to many relays, or congregate around specific ones. User profile data will list the relays they listen on, but this suffers from the problem of sticky defaults and makes client authors the kingmakers.

There's lots of centralization pressures like this that the protocol maintainers don't have a good answer to. They tout the simplicity of the protocol, which is often a virtue, but they overdid it and made the protocol too simple to achieve its goals.


> protocol spec expressly forbids relays from forwarding messages to each other.

That statement is false and if you disagree: please provide a source.

There is no such restriction on the NIP (protocol guidelines). I have been writing NOSTR software since years and there was NEVER such restriction in place. In fact, wouldn't even make sense because some relays (e.g. Primal) are super-aggregators for smaller relays.

> users trying to reach each other need to shotgun messages to many relays

This is a false statement. NIP 65 provides a list of which servers the users declares to be using. This way readers for that user know at which door (server) to knock and ask for updates.


there are relays built exactly for this, for rebroadcasting. You can do whatever you want in nostr btw.

this is also not entirely needed since you publish a list of relays you use, and so clients publish notes for you to them and you read from the relays of people you interact with.

there are many different people/teams working on different aspects of nostr, there are no protocol maintainers.


it doesn't explicitly forbdit it. It just doesn't spec it out, because it doesn't need it for the protocol to work. There are already many relays and clients that do just this.


I think GP's point (which I agree with) is that the functionality is almost essential. When it's not part of the spec, then you end up with differing off-spec implementations, and again, centralization risk.

This would be like the HTTP protocol not defining `POST`, and leaving it up to servers and clients to implement it based on however it feels like.

I really like the ideals behind Nostr, but I think its implementation and execution could be better.


It would be more analog to HTTP not specing out how CDNs should work, or Usenet not specing out how DejaNews is going to work. It's infrastructure stuff neither the client nor the simple server has to care about.

The Nostr spec covers what matters, cryptographic identities and unique message ids, that make dumb relays that duplicating messages from elsewhere possible (an area where HTTP or Activity Pub fail at).


It's actually more analogous to HTTP saying that proxies and reverse proxies are banned.


But part of the protocol is that user profiles also list the relays where to find their content (where their posts go to) and a second user connects to those relays to fetch their content directly?


> sticky defaults... makes client authors the kingmakers.

Isn't this true of any protocol (to a greater or lesser degree)?


> spec expressly forbids relays from forwarding messages to each other.

And how do you exactly envision enforcement? Ohhhh, you copied some bits, you going to jail!!


False. The protocol doesn’t forbid it. Theres actually a negentropy NIP for relay sync.

Did you read the actual protocol or are you just making things up on HN?


I also remember reading somewhere that relays aren't allowed to copy other relays.


There are relays built with copying from other relays as a main feature. Personal use only https://github.com/barrydeen/haven This one will copy every note of every person you follow, plus their follows. https://github.com/barrydeen/wot-relay


Well put, that's exactly what I think of Nostr as well.


What the hell are you talking about? What forbids?

The idea of nostr is that you can do whatever the you like. An open protocol. There is no forbidding of anything


> There is no forbidding of anything

Promise not to prosecute me if I spam the network?


I couldn't disagree more. Juniors have very poor design sense and can't guide the AI to land in the right spot. Consistently on my team the developers who are the most reliant on AI are causing me the most trouble. They produce a lot of code but constantly make the same mistakes and can't seem to learn and improve their own design skills, or are doing it at a snail's pace.


This seems to point to a future crash where we run out of people with the skills to do the work.


I agree with the sentiment. But is that exclusive to junior engineers? Curious if you think that the same problem exists with senior engineers or not.


A help with this is the sun is to the left and it seems to be midday, so you could answer the "cardinal direction" question just from the picture with "west ish", which is what it turns out to be.


I tried to use the sun info , but honestly I couldn't at all,

thx for the tip


It'd probably be hard to do directly/algorithmically, but the shadows from the trees is what I was looking for visually.


There's a reason for this. Rob Pike was asked about it and said that syntax highlighting reminds him of the bright colors of children's toys and he personally disables it so that he can focus on the text.

I don't know why it's still like that but that's the original reasoning.


> Syntax highlighting is juvenile. When I was a child, I was taught arithmetic using colored rods (http://en.wikipedia.org/wiki/Cuisenaire_rods). I grew up and today I use monochromatic numerals.

https://groups.google.com/g/golang-nuts/c/hJHCAaiL0so/m/kG3B...


That must be why traffic lights and electrical wires and transit maps are all black and white...


That’s an interesting point.

The reason traffic lights are colored is because they are showing distinct states of the same thing and the color is the means of differentiating.

Same for transit maps: different routes are colored to distinguish them from other routes, which is especially useful if they overlap.

But that’s not what syntax highlighting does.

The equivalent of your examples would be to not highlight the syntax at all, but only use color coding to distinguish variables.

The equivalent of how syntax highlighting currently works for your examples would be if the light fixture was one color, and the light pole was another, but then all the actual lights were the same color.

I actually think highlighting only the variables with distinct colors could be extremely valuable. Would certainly help avoid mistakes with nested i/j loop counters.

Edit to add: come to think of it, it would have been even more valuable in Go, until recently anyway. The variable color coding would expose the common loop variable instance bugs, because a programmer would be instantly puzzled by the unexpected coloring.


That does exist, it's called semantic highlighting.

In any case, my analogy was not perfect, but neither was Russ's! The point is it's totally normal and not "childish" to use colours to help distinguish things. Traffic lights do not technically need colours (you can use the position of the lights - I assume that's what badly colourblind people do). Nor do transit maps technically need colours - you could just label the lines, or use patterns.

https://www.flickr.com/photos/gywst/1407078279

It's completely absurd to say that colours are childish because they can help children.


Your comment makes me think of LabVIEW - if you're not familiar with it, it uses a visual programming language ("G") in which data flows down wires. The color of the wire indicates the wire's data type (blue for ints, orange for floats, green for bools, pink for strings) and the width of the wire indicates the dimensionality of the data (thin line = scalar, thick line = 1D array, double-thick = 2D array). The color is more than decorative or even assistive - it's essential to understanding the program.


I still wonder why every open-source visual programming language is either a toy for teaching or straight up awful, often not implementing but even loops, when LabVIEW has been doing it right for decades.

Despite its huge size and it installing several services that constantly run in the background, it's still one of my favorite "languages" of all time. It's the only one I've ever seen people going from never having programmed before to making simple but meaningful contributions in within a single day.


> The reason traffic lights are colored is because they are showing distinct states of the same thing and the color is the means of differentiating

I wonder if traffic lights were invented today, would it be just one light changing colour?


As a red-green colourblind person I hope not!


There are a bunch of identical things on my screen called lines. They contain a bunch of mostly identical things called tokens. Syntax highlighting uses different colors to identify the different purposes of all of these tokens blasted onto my screen.


Wow, that thread is a piece of work

> Gofmt was written to reduce the number of pointless discussions about code formatting. It succeeded admirably. I'm sad to say it had no effect whatsoever on the number of pointless discussions about syntax highlighting, or as I prefer to call it, spitzensparken blinkelichtzen.

> When I was a child, I used to speak like a child, think like a child, reason like a child; when I became a man, I did away with childish things.

I sincerely hope Rob Pike was being sarcastic/ironic, because otherwise, he sounds insufferable



he is insufferable. it's his thing lol.


> because otherwise, he sounds insufferable

Oh come on, I like syntax highlights, but this is not "insufferable". It's just opinions expressed strongly, with probably some tounge-in-cheek


He sounds insufferable because he's calling syntax highlighting childish. Not simply politely saying it's not his thing. It's like he can't understand why anyone else would use it, or isn't bothering to. Maybe there's more nuance to what he said, but it's not obvious from the quote.


"I prefer to code in black and white only"

"Everyone who uses syntax highlighting is a child"

I mean, maybe he was genuinely trying to make a joke, but I'm well past the point of assuming random strangers are joking when they're being offensively dumb.


I want to downvote this for being one of the stupidest things I've read this week but you're just quoting it so I guess I'll just seethe silently.


never discount the possibility of so called brilliant people having dumb AF takes.


this is hilarious


What is childish is holding up one guy's editor preferences as a religious sacrament when 99.9% of your readers have different preferences.


Well, there's kind of a precedent at least...

> Gofmt's style is no one's favorite, yet gofmt is everyone's favorite.


Unlike formatting preferences, the colours I use in my editor don't effect others. That said I might not mind living in a world where everyone used the same highlighting scheme, as long as it was reasonable ;)


That's been a major success


It basically revolutionized the entire industry and thank god.

Opinionated standard formatting is now the default.


That makes sense as a personal preference for him, but it's odd for that to still be the company/project stance. Like, surely he knows he's the minority for not wanting highlighting?


Oceania had always been at war with Eastasia.


Apart from the fact that your comment is against the rules: please don't trivialize that quote. It has a profound meaning, and is completely out of place here.


There is a thread linked where we literally see gophers express the belief that they do not like syntax highlighting, and mock those who do - because Great Leader - born in 1956 - does not like syntax highlighting. Its called double think.

https://groups.google.com/g/golang-nuts/c/hJHCAaiL0so/m/6STj...


that's an extremely odd explanation and it makes me think that he has some hidden PTSD. it's also insane that one person's preference trumps the rest of the world's.


He also doesn't capitalize his sentences.


I like to alternate. or use constructions that make it ambiguous.


Maybe he was talking in private.


Welcome to Go as a project.


Child hood trauma led to if err != nil and now the rest of us get to share that trauma? Makes slight sense I guess.


Born out of C++ trauma apparently, for context


I understand and respect this position. I think syntax highlighting is a highly subjective matter, bordering on personal preference with regard to shell interactions, editor configurations, bindings, shortcuts, snippets, and the like. It's also... insignificant somehow, like quibbles over formatting rules that Go settled once and for all with `go fmt`.

I often prefer not to enable syntax highlighting just for color. Occasionally I'd choose some minimal theme that only highlights string literals and keywords. So it has two or three colors. But some of the color schemes I see are a festival of lights where every special element of syntax has its own color. I don't understand how that is supposed to help me parse anything and why the rules are complex. The `range` keyword needs to be purple, and `chan` must be navy blue. Why exactly? And every site has a different color scheme? There is no consensus, and there shouldn't be.

For a serious community-driven project like Go, dealing with the question of syntax highlighting is strange. The creators deliberately avoided the questions of IDEs and editors for Go, leaving them to the community. I think the same principle applies here.


> The creators deliberately avoided the questions of IDEs and editors for Go, leaving them to the community. I think the same principle applies here.

Exactly. This is fundamentally about accessibility (in the broadest sense). Do people have opinionated screenreader settings they like to force on others too?


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: