Hacker News new | past | comments | ask | show | jobs | submit | jkrejcha's comments login

At least on Windows 10, which I still run, pretty much all of the "explorer is slow" issues are caused by shell extensions.

The problem is a lot of the times they make poor assumptions (like "if a process is running it'll respond instantly") and honestly the tools that exist for determining what is at fault are... well effectively nonexistent[1]. This goes even for 1st party shell extensions. Part of the problem is these almost inherently violate what I consider one of the golden rules of GUI programming (don't block the UI thread) and there's a lot of historical reason for this, but it is often the cause of stuff like "explorer.exe uses 100% CPU" or "right-click takes 30 seconds"

[1]: There's a SuperUser post in which the recommendation is effectively: manually binary search for culprit shell extensions yourself (https://superuser.com/a/577935/312312).


There's nothing about that that requires JWTs/signed cookies.

You may need JWTs or their moral equivalents for 3rd party services but, especially for 1st party services, session identifiers are a fine enough scheme that are oftentimes implemented more securely and have the same amount of statelessness (at least from a REST perspective) as a JWT.

Not that cookies are allowable within the constraints of REST anyway due to violation of the uniform interface/stateless constraints, but pragmatically cookies have the most user agent support, and when used as just an session identifier, are relatively close to following the constraints and are much better supported than using the Authorization header or whatever[1].

Statelessness (the lack of "sessions") refers generally to the fact that the client and the server don't need to share state, i.e. the client has all it needs to make a request rather than, like, an "authorization context" or something (which is what a "user session" colloquially is). Unfortunately, the difference in the way the terms were used kinda led to this confusion which made people think that they weren't doing REST unless they were using JWTs or signed cookies.

It's the difference between storing the shopping cart in a cookie or what have you vs. creating a shopping cart resource. In the former scenario, the server has to track where in the (often implicit) state machine the current client is[2], whereas in the latter, the client has all it needs (a URI, authz token, etc) to make the request and all the state is stored server-side.

[1]: If browsers had better support for setting the Authorization header somehow, this would almost certainly just be a "best practice" that we take for granted. Automated clients with API keys tend to be better in this regard.

[2]: And there are significant disadvantages to doing it this way, if you've ever lost your cart or got those weird "session expired" errors after hitting the back button, you've ran into the pitfalls of doing it this way.


Yeah also in general the WFC code is a bit dated and not very secure.

This actually reminds me of two very interesting bugs which used together basically make it so that you can play WFC games (basically just Mario Kart Wii, nowadays) as simple as changing the DNS settings on your Wii

1. Firstly, as long as you set a particular field in the certificate, it just is completely happy with an invalid cert. (This was fixed by the NWC library by the time it was released In Korea, notably, although this bug was present in DWC for a long while.

(Aside:

I actually suspect that this bug was present in the RVL SDK (used by games and such on the PPC), but also is caused by the same cause as the signing/Trucha bug[1]. While the latter is a IOS specific exploit, it wouldn't surprise me if the same code was used in both this and DWC (the networking library). Given that Mario Kart Wii has an associated IOS version of IOS36[2], but DWC code isn't part of IOS, my hunch is that they used either the same or similar validation logic OR both bugs were squashed a part of some security related cleanup.

I haven't actually gone through the reverse engineering effort to confirm this yet, but given that this doesn't work on the Korean version of MKW, which notably uses a later version of IOS and other libraries, my hunch is that those bugs are one in the same. The fix timing at least seems interesting to me. Anyway side note over.)

2. The networking library also has an RCE caused by a buffer overrun, basically from the first message it has a length that's unchecked and the DWC library blindly memcpys data from the packet. This is kinda why it's important to have some sort of patchset that fixes these bugs (because the operating system and libraries ship with the game and you can't update those except for in memory).

The culmination of this is all you have to do is

1. Change your DNS settings on your unmodified Wii to point to a specified DNS server.

2. Start Mario Kart Wii (probably, although some other games work too), open up WFC

So that the game...

3. Does a DNS lookup for the WFC server which intentionally links to a 3rd party server

4. Passes validation of a bad cert which intentionally sets one of the fields to a null value in order to make the Wii accept it

5. Receives a message that contains an exploit which patches the game in memory to fix the known RCEs and setup URLs to resolve to different domains instead of using the old WFC ones among other things (such as cheat reporting that is all client-side based, etc)

all so you can play Wii games (probably Mario Kart Wii) online 11 years after WFC shut down for good :)

[1]: https://wiibrew.org/wiki/Signing_bug

[2]: https://wiibrew.org/wiki/IOS36


Yeah, the DS probably is the first one that has enough inbuilt support since it has support for Wi-Fi out of the box although Linux seems a bit heavyweight for this perhaps(?)

DSLinux exists but you need the RAM expansion cartridge. from what I found it supports networking but you've only got 8MB minus the space used by the system. and an ancient binary-only toolchain to wrangle with

Raymond Chen has an article on this as well[1], the TL;DR is that you have 3 different upgrade paths (i.e. the text mode portions for a clean install, the Win16 portion for a 3.1 -> 95 upgrade, or the Win32 portion for a 95 -> 95 upgrade)

[1]: https://devblogs.microsoft.com/oldnewthing/20241112-00/?p=11...


With regards to I/O, there are generally any number of weird errors you can run into, file not found, directory exists, host unreachable, permission denied, file corrupt, etc etc. Like anything file related will be able to throw any of those exceptions at almost any point in time.

I think IOException (or maybe FileSystemException) is probably the best you can do in a lot of I/O cases unless you can dedicate time to handling each of those specially (and there's often not a lot much more you can do except for saying "Access is denied" or "File not found" to the user or by logging it somewhere).


There are fatal IO errors and non-fatal. Most of us don't care about the non-fatal case, but mainframes have the concept of "file isn't available now, load tape 12345", "file is being restored from backup, try again tomorrow" - things that your code could handle (if nothing else you should inform the user that this will take a while). There is also the "read off the end of the file" exception which in some languages is the idiomatic way to tell when you have read the whole file.

But most IO errors are fatal. It doesn't matter if the filename is not found, or the controller had too many errors talking to the drive and gave up - either way your code can do nothing.


> That's sort of the crux of the issue: what constitutes exposing a pointer?

A lot of the people who subscribe to "optimization 3 is clearly wrong" make the argument that the answer is... everything is always exposed, whether it be (uintptr_t)0x42, some valid stack pointer, or anywhere else for that matter. This certainly tracks with the model that many C (and systems programmers, more generally) expect the language semantics to represent.

It disables a lot of optimizations to make these assumptions, but the argument generally is that many of those optimizations were likely extremely tenuous in the first place and have potentially dubious benefits outside of contrived benchmarks. This also was the point of the "restrict" and "register" keywords to tell the compiler "hey you can assume these things don't alias with each other" or "hey use registers for this variable", etc.

It's clear that a demand for this type of language semantics exists, as most large projects you can probably think of have flags that disable a lot of the alias assumptions (everything from Linux to Firefox to Clang (in some cases) to Chrome, etc) and some compilers (such as MSVC) don't bother with many of the alias assumptions in the first place (which affects anything built with those compilers).


When "making everything exposed" you can not even do register allocation. I hope you agree that this has some effect "outside of contrived benchmarks". Of course, you could require programmers to add "register" everywhere, but this ship has sailed decades ago.


> A lot of the people who subscribe to "optimization 3 is clearly wrong" make the argument that the answer is... everything is always exposed

For anyone reading this, note that that's not what I'm arguing -- my argument very specifically depends on the exposure of q: https://news.ycombinator.com/item?id=42906600


Yeah, this does make the "read pointer from user input" case (which is actually sometimes useful) a bit weird however but it seems pretty obvious that ptr-to-int should probably be exposing at the very least


> Yeah, this does make the "read pointer from user input" case (which is actually sometimes useful) a bit weird

I think it works out quite reasonably and elegantly, actually.

If you can prove that an address wasn't leaked -- then you can assume the program behavior is independent of any pointer read from user input, and thus optimize as if the pointer wasn't read from user input.

Otherwise, you assume the address was leaked, and thus must act as if the target might overlap said address.

> however but it seems pretty obvious that ptr-to-int should probably be exposing at the very least

I don't think that's necessary, but it's certainly a valid way to write a compiler. (I say this because I don't think (void)(uintptr_t)ptr; should be considered exposing, for example.)


Because for many conforming (not necessarily strictly conforming) programs, should and does are different things and many programs are written to be conforming rather than strictly conforming (hence why most non-trivial sane projects (including Clang in some cases, amusingly enough) builds themselves with flags that disable many of these style of checks).

The argument goes like this

Assuming that p lives at 0x0 and q lives at 0x1 (this is the only interesting case anyway, if this isn't the case the program clearly prints nothing)...

p[0] = {0} q[0] = {0}

Line 1: ip = undefined, iq = undefined

Line 2: ip = 0x0 + 1 = 0x1 (assigned), iq = undefined

Line 3: ip = 0x1, iq = 0x1 (assigned)

Line 4: ip = 0x1, iq = 0x1 (ip == iq, so take branch, goto line 5)

Line 5: ip = 0x1, iq = 0x1

- p[1] = 0x10.

- Because p([0]) is at 0x0, p[1] must be at 0x1

- (address of p[1]) = 0x1 and (address of q[0]) = 0x1 and 0x1 == 0x1

- Therefore because these pointers have the same address, these two objects obviously must alias each other at this point

Line 6: print(q[0]); -- should print 10 because p[1] = 10 which means q[0] = 10 which because they're at the same location

The argument against this is that a pointer really is a address and some hidden data that tracks what allocation it was, therefore you can't treat p[1] as equaling q[0]. I find myself tending to agree with the original argument (this is C after all) rather than this counterargument, but I understand it nonetheless.

This touches a pain point on the evolution of C in general. C is a lot of the times the domain of stuff of it being a "portable assembler"[1] which many programmers who are writing C code for things like operating system kernels and other systems level code expect and like, but because a lot of these make non-strictly conforming programs, many popular compilers are writing optimizations in their evolution that attempt to target strictly conforming programs only at the expense of... well a lot of the reason people use (and have to use) C.

[1]: Many might take issue with the characterization of C, but it's how it was characterized even in things like the 1st ed of "The C Programming Language".


Note: the fact that this was actually done through q[0] rather than p[1] isn't really material for the argument, but this was an error I made in the original argumentation (that I can't go back and edit now) so just wanting to make that correction.

If anything, it makes the argument stronger, as the program as written is valid syntactically and semantically rather than relying on undefined behavior.


Ah, I see, it's trivial out-of-bounds array access, and the compiler's inability to notice that. The cause of innumerable data corruption bugs, crashes, and exploits.

I wish C did not have implicit pointer aliasing; it already has explicit aliasing via `union`. (And a different handling of nulls. And no UD. And a pony. Sadly, I don't have a time machine to travel back to 1969.)


So worth noting that the original code actually exhibits no undefined behavior as written (only q is accessed). I erred in my original argument by looking at the "first optimization" being applied.

I'd still argue that the third optimization would be incorrect here, if a program can determine that p+1 = q and replacing q with p+1 is legal, then it obviously knows that those two locations alias (kinda by definition of being able to replace that) and therefore can't apply the 3rd optimization.


>I'd still argue that the third optimization would be incorrect here

That's my take as well, to apply it more defensively, any writes cannot be proven in-bounds should be considered clobbering q's state.


I mean yes in this case the problem is you're doing a dereference out-of-bounds, which makes a program not strictly conforming.

The problem tends to be is that the real world is much more (essentially) complex and the domain of C is that which needs to be able to deal with that essential complexity, which includes things like type punning and int-to-ptr casts and the like which makes programs not strictly conforming. As it stands, a lot of these rules make it impossible to implement a lot of... C libraries... in ISO C, let alone things like operating system kernels and the like.

> And no UD

I assume this means undefined behavior? But in any case, undefined behavior is okay to have in a language, it's good for when there are no portable semantics for a particular operation or failure mode[1]. It's generally essential (although some undefined behaviors would definitely be better served being implementation defined or unspecified).

It is also okay for programmers to eschew this portability (so if they want to narrow their focus from "anything that can run strictly conforming C" to a superset that targets say a specific memory model or set of processors).

[1]: For example, at an extreme example, what happens if the CPU starts burning up? Obviously the standard can't describe such semantics for how a program should behave in the face of this.


I agree, type punning exists, this is why I mentioned `union`, invented to make some type punning easier. An explicit mechanism like that is essential. Doing it implicitly is, to my mind, too dangerous to be beneficial.

Same with undefined behavior. I'm fine with implementation-dependent behavior (like what size is `int`, etc). But modern compilers see UD and feel free to do anything, even removing the code in question. Such things should not be silent, or even a warning. Instead it should be something like `unsafe` sections in Rust: "I know what I'm doing, the usual safety assumptions are irrelevant here, do what I literally say". Doing weird things is sometimes required e.g. to handle memory-mapped hardware registers, etc. But such areas should be clearly delineated, for they are usually few. They also tend to not be portable.

A CPU burning up, hardware malfunctioning, etc is usually a situation where all bets are off. An unanticipated pointer aliasing in a trivial program running on perfectly working hardware is a different case.


> which many programmers who are writing C code for things like operating system kernels and other systems level code expect and like

Strong agree. Isn't this the whole point of C? I don't have any insight into why the standards committee could see it any other way. It seems like the whole situation got hijacked by optimization writers who had somehow lost touch with their users. But maybe there is a more coherent explanation of how this happend.


Do you suppose that most C programmers enjoy the fact that random aliasing and free out-of-bounds array access can lead to silent data corruption, as shown in the example?


This example isn’t showing silent data corruption due to out of bounds array access. It is showing that LLVM and others do not correctly apply optimizations to the original code. The program is correct and does what it’s supposed to in the original example.


You mean, accessing an array member past the declared array bounds and overwriting another variable that happens to be allocated right next to it is what the programmer should have consciously planned to do here?

IMHO, a planned aliasing would look like a union. The declaration we see does not even guarantee that q follows p in the address space, or maybe even that p and q are adjacent.

(I know that int[0] sort of means "no bounds", and I think it should be an error for a stack-allocated variable which ought to have definite bounds.)


I'm not sure what your point is here.

With respect to "the example". The code in the linked article does not perform out of bounds array access. `ip` points to one-past-end, which is legal, and it is never dereferenced (in the original, correct, user written code). All issues described in the article relate to faulty compiler transformations.

On the other hand, if you are arguing that the language should protect the programmer from "silent data corruption" by limiting their freedom to implement correct low-level memory access or pointer manipulation (correct, but not easily provable by the compiler infrastructure) then I disagree. C is not a safe language and it should not protect the programmer. It is not acceptable to me that the compiler writers are overly conservative about which correct programs are expressible in C, and which can only be written in assembler. In C I am willing to take the risk of "silent data corruption" in exchange for the freedom to write the programs that I want to write. If for some project the risk/benefit equation weighs in a different direction I will choose a different language.


Responding to the Discord point, charitably, I think the person you're replying to is saying that Discord is a poor documentation platform that has some fatal flaws, and I do think it has unfortunately replaced web forums in a lot of contexts, which means a couple things:

1. It's unsearchable and opaque: you can't really do anything but the most basic of searches on Discord and getting support or finding previous/similar questions can be incredibly difficult. This also has the strong disadvantage of being unarchivable with makes the next issue more important.

2. It's controlled by a single entity and the viability of Discord, from both a service provider perspective and a community perspective is uncertain. For all we know, Discord, the company, could die without any warning, or there could be policy decisions which make hosting communities untenable. If that happens, it'd be incredibly difficult to get stuff off of there. With more decentralized communities, or even centralized, but internet-visible ones, there are potentially archives and backups that can help restore information.

This, imo, makes it not a great choice for a software project's forum or whatever. Maybe general IRC-style chat where conversations tend to be relatively ephemeral anyway? Sure. But it's not a great forum or wiki replacement imo.


It's usually used to prevent caching from older caching systems.

Fun fact: this specific value of Expires is the birthday of Sasha Schumann, the person who added this code to PHP[1][2].

[1]: https://stackoverflow.com/a/8194500/2805120

[2]: https://github.com/php/php-src/blob/master/ext/session/sessi...


Join us for AI Startup School this June 16-17 in San Francisco!

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

Search: