Recent submissions

Comment · 4 years ago
Comment · 4 years ago
Maybe that's the subject for the next blog post, but I think the main reason cancellation causes more troubles than ordinary IO problems, is that with ordinary errors you assume that the resource that suffered the errors is down and don't care about its precise state, while with cancellation the resource is perfectly OK and you want to continue using it.
Comment · 4 years ago
Its more that in Rust, async cancellation will occur exactly at await points, which are generally when you do IO.
Comment · 4 years ago
The one system I've seen work is when each medium team (of say, 20-36 people) has a "devops team" of say 4-6 people that is responsible for all the infrastructure stuff, while the rest of the people can focus on product work. It's just that keeping track of infrastructure is much more efficient as a full-time job rather than a 10%-time job.

It's important to do the org engineering right so that the infrastructure people and developers feel responsible for each-other and work well together - ideally both teams should be able to open PRs against each other's code if needed and get help any time.

If the org engineering isn't done right, devs write code that pages the ops and the ops don't respond to devs quickly enough for things to work well ("traditional ops").

Comment · 5 years ago
The main difficulty I have with this method is that I don't have an easy way to pass a Python array to bash as an array of parameters.
Comment · 5 years ago
When calling shell from bash (to use pipes etc.), I found it useful to pass variables via the environment, like this:

    def safe_call(command, **keywds):
        return subprocess.check_call(f'set -euo pipefail; {command}', shell=True, env=keywds)

    safe_call('command1 -- "$bar" | command2 --baz="$baz" | command3 > "$output_file"', bar=bar, baz=baz, output_file=output_file)
Comment · 6 years ago
Not that surprising: CPUs can perform scalar in parallel with computation, so I would expect a few scalar ops to be free in any code that is not scalar-bound.
Comment · 7 years ago
> The described attack utilized a malicious router.

I understand how cross-interface packets can be used maliciously. I'm just trying to figure out the non-malicious use cases for them.

Comment · 7 years ago
What's the configuration you're talking about? In the Wifi+Ethernet case, how do the routers know to send the packets towards the "right" interface, without the computer having the "right" IP address?

I mean, suppose the computer has WiFi IP address 10.0.0.3 & Ethernet IP address 10.0.0.5, then after NAT the return packets will go to 10.0.0.3, and therefore should go to the WiFi interface, not to the Ethernet interface (or, if they don't, how do they know which interface they should go to?).

Comment · 7 years ago
Is there a place where I can read about these cases?
Comment · 7 years ago
How is this supposed to work? How will the packet destined to the WiFi IP address get to the Ethernet interface?
Comment · 7 years ago
> nping --tcp --flags SA --source-ip 192.168.12.1 --dest-ip 10.8.0.8 -- rate 3 -c 3 -e ap0 --dest-mac 08:00:27:9c:53:12

Why is Linux accepting packets coming from one interface into an IP address belonging to a different interface? It feels like it is "forwarding" the packets internally, but `ip_forward` is turned off.

Is there any case where this behavior is legitimately useful?

Comment · 9 years ago
From the outside, it feels that most exploit chains on modern systems rely on 4 mostly-independent steps: 1. code execution in a worker process - typically a memory corruption 2. ACE in worker process to ACE in unsandboxed process 3. code execution in unsandboxed process to local root 4. local root to persistence

Finding (1) (3) and (4) is old-school exploit development - a combination of looking at fuzzers, looking at code, looking at bug reports, and memory exploit development (which is a black art I'm not familiar with). So persistence and luck. Be lucky 3 times and you have 3 steps. If you were an organization I suppose you could have 3 separate groups or buy from 3 separate blackhats.

I'm less familiar with the "worker process to user process" part, which tends to rely on combining a few vulnerabilities (in this exploit, 2 + 1 broken hardening), but it's probably similar.

Comment · 9 years ago
For example, accidentally sharing a lock-less cache or a non-atomic reference counted pointer between threads.

For example this code, which tries to send a reference-counted pointer between threads, which can cause the reference counter to become unsynchronized and random use-after-free:

    use std::thread;
    use std::rc::Rc;

    fn main() {
        let rcs = Rc::new("Hello, World!".to_string());
    
        let thread_rcs = rcs.clone();
        thread::spawn(move || {
            println!("{}", thread_rcs);
        });
    }
Is detected by the compiler and causes this error

    error[E0277]: the trait bound `std::rc::Rc<std::string::String>: std::marker::Send` is not satisfied in `[closure@src/main.rs:8:19: 10:6 thread_rcs:std::rc::Rc<std::string::String>]`
     --> src/main.rs:8:5
      |
    8 |     thread::spawn(move || {
      |     ^^^^^^^^^^^^^ `std::rc::Rc<std::string::String>` cannot be sent between threads safely
      |
      = help: within `[closure@src/main.rs:8:19: 10:6 thread_rcs:std::rc::Rc<std::string::String>]`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<std::string::String>`
      = note: required because it appears within the type `[closure@src/main.rs:8:19: 10:6 thread_rcs:std::rc::Rc<std::string::String>]`
      = note: required by `std::thread::spawn`
Comment · 9 years ago
Engine failed during qualification testing, not acceptance testing - so it's a new design.

If you don't occasionally blow an engine up on the stand you are either not testing hard enough or not pushing the limits hard enough.

Live from the official Hacker News API

A modern reader for Hacker News · SvelteKit + Skeleton