Hacker News new | past | comments | ask | show | jobs | submit login

I'm actually not sure what that passage is trying to imply at all, I've not seen any prior remarks that Spectre has that sort of implication. AIUI, Spectre lets you read arbitrary memory, not write it, so I'm not sure what they mean by "bypass the type check" and "craft arbitrary pointer". Here's another quote later on:

> But Spectre could theoretically involve any branch that enforces security properties, like the branches used for type checks in JavaScriptCore.

I get the impression this is all referring to JIT'd JS, not to the C++ (or Rust etc.) code powering the underlying engine (where all typechecking is done at compile-time anyway). Of course having the JS engine written in Rust would still allow the same, but it's been known for a long time that Rust's typesystem can't do squat to verify the correctness of dynamically-generated code; it's part of the reason why Mozilla only bothered to kick off a rewrite of Gecko and not SpiderMonkey.




It's common for C++ code to do type checking dynamically. In WebKit we do this by rolling the type checks ourselves. With RTTI, you can do dynamic type checks using the build-in dynamic_cast primitive. Our changes, particularly the ones having to do with pointer poisoning, are meant to protect C++ code that does dynamic type checks in addition to JIT'd JS code that does dynamic type checks.

Rust's `match` statement is a dynamic type check just like C++'s `dynamic_cast`. I bet you it's implemented using branches.


> Rust's `match` statement is a dynamic type check just like C++'s `dynamic_cast`.

I wonder if this is a case of a difference in terminology, because enum variants (the branches in `match` expressions) aren't considered types in Rust, and have no interaction with Rust's typechecker (Rust always has to assume that every instance of an enum can be any variant, even in cases where we as the programmer know that only one variant is possible).

At the same time I'm still unclear on what the OP is trying to imply. To reiterate, AIUI Spectre can only read, not write, privileged memory, so it's impossible for a Spectre attack to trick the engine into believing that (to use Rust terminology) an instance of an enum is a variant that it isn't. Am I incorrect?


The idea is to read random things as follows:

1) Write a function that reads the "nth" value out of an array in JS.

2) Call the function a bunch of times with JS arrays.

3) Pass an integer to that function for the "array" value. This would normally end up throwing. But before it does, the CPU might speculate the VM-internal typecheck as "it's going to be an Array, like the previous 1000 times" and end up doing an "nth value" read out of a memory address that you fully control (by changing which integer you pass).

That is, you can use speculative type confusion in the VM to allow precise control over what memory addresses get accessed and how for your timing attacks on the cache.


Hypothetical example pseudocode:

    if is_pointer(pt):
        // do pointer-based stuff
    else:
        raise error
If you train the branch predictor to expect a pointer, it will speculatively treat arbitrary values as pointers until it can determine that they are not. So you can pass in any value and get it treated like a pointer for the duration of the window of speculative execution.

Any conditional branch is potentially vulnerable, an attacker just needs some sort of side effect from speculative execution that persists after rollback.


Thanks for the reply, I think I've got it now: the ultimate attack is still to read arbitrary memory, and the part about defeating typechecking and crafting a pointer is intended to expand the range of memory that can be read.


Serious question, is it even possible to have something like ‘match’ or ‘dynamic_cast’ in a programming language without using branches under the hood?

I’m not talking about research/example microprocessors that don’t have equivalent instructions. I mean isn’t branching a fundamental logical construct and we either do it in hardware with branch assembly operations or emulate functional equivalents of them in the software that runs on the microprocessor some other way? Or is there some clever/old logical/mathematical “alternative” to branches?


Consider something like this:

    struct __internal_variable {
        uint64_t type;
        void *data;
    }
    
    uint64_t __last_type = [number of builtin types];
    
    whenever you create a new type: increment __last_type and associate that type with the number;

    uint64_t typeof(__internal_variable var) {
        return var.type;
    }
    
    function[__last_type] typechecks;
    functions[] = void function(uint64_t type) { failure; }
    functions[int] = void function(uint64_t type) { success; }

    functions[typeof(myvar)]()
If typeof(myvar) is int, then the function that returns success will be called, otherwise failure, no branches involved! Yes, it's a kludge, but it kind of works.

---

Of course, you don't actually need this trick. If you put enough effort into it, you can essentially compile anything to c, after which you can just compile it with the movfuscator[1]

1: https://github.com/xoreaxeaxeax/movfuscator


That's basically a jump table. Still vulnerable to spectre (variant 2) without mitigations, btw.


It is even worse then switch/match statements. The jump tables can be used to train CPU to jump to a wide variety of addresses than switch/match statements so mounting a speculative execution attack is simpler.


true. Then again switch statements (and I assume match statements in language that have them) are often compiled down to a jump table...


The problem isn't the branches it's the speculative execution


Yes,it's already implemented as a poc - just look at movfuscator


AIUI you create a pointer by issuing an array OoB. The check against this is a brach statement followed by the array OoB. However, the CPU starts executing the array OoB while the check is happening and the memory in the pointer gets loaded into cache.

This is an architecture bug so any security check that relies on simple branching is potentiality vulnerable.




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

Search: