Mordak's comments

Mordak | 5 years ago | on: Rust Foundation: Hello, World

I feel like rust has some good sweet spots right now. I care about these but maybe not everyone else does.

- Parsing untrusted inputs. nom[1] is a joy to use, and lets you kind of effortlessly and fearlessly process random input from wherever and turn it into useful data structures. If your data is very regular or in a standard format, then serde[3] is very hard to beat if it just boils down to 'derive(Deserialize, Serialize)' on your Rust struct.

- Bulk data processing. rayon[2] makes pegging your machine easy if you have a lot of work do to, and the Rust semantics around thread safety, data ownership, and explicit copying make it kind of trivial to reason about how your data gets from input to output and tuning it to be crazy fast.

- Generic systems language. Maybe this one is personal, but I find it's more productive to write generic cli applications and whatnot in Rust over C, ruby, or python. There are some nice libs to make part of this pleasant (structopt[4]) but this really boils down to reliability. Because Rust makes it obvious where things can fail so I can deal with it I have way higher 'just works' outcomes in Rust than other languages. I might spend slightly more time making it compile, but I spend basically zero time debugging runtime failures and this is kind of indescribably amazing.

[1] https://docs.rs/nom/6.1.0/nom/index.html

[2] https://docs.rs/rayon/1.5.0/rayon/

[3] https://serde.rs/

[4] https://docs.rs/structopt/0.3.21/structopt/

Mordak | 5 years ago | on: Chromium project finds that 70% of security defects are memory safety problems

Something like the cxx crate[1]? You specify your shared objects between C++ and Rust, and it spits out code for both sides.

The guy who maintains it said in the reddit thread[2] about this same topic that the Google people have been sending him good PRs, which is presumably related to integrating Rust into Chrome.

[1] https://crates.io/crates/cxx [2] https://reddit.com/r/rust/comments/gpdorw/the_chromium_proje...

Mordak | 5 years ago | on: Chromium project finds that 70% of security defects are memory safety problems

Nah.

We built a new project in all "modern C++". It is 100% shared_ptr, unique_ptr, std::string, RAII, etc. It initially targeted C++17 specifically to get all the "modern C++" goodness.

It segfaults. It segfaults all the time. It is entirely routine for us to run a new build through the CI process and find segfaults. We fuzz it and find dozens of segfaults. Segfaults because of uninitialized memory. Segfaults because dereferencing pointers. Segfaults because running off the end of arrays. Segfaults because trusting input from the outside world ("the length of this payload is X bytes").

This is where the "modern C++" people tell me we must be doing it wrong. But the reality is that "modern C++" isn't as safe or as foolproof as the advocates say it is. But don't take my word for it - this whole thread is about Google people coming to the same conclusion.

Meanwhile I can throw a new dev at Rust and watch them go from zero to works in a week or so, and their code doesn't segfault, doesn't panic, and actually does what it is supposed to do the first time. Code reviews are easy because I don't have to ponder the memory safety and correctness of every line of code. Reasoning about unwrap() is trivial. Finding unsafe {} is trivial (and removing it is also usually easy).

Mordak | 5 years ago | on: Re: Integrating "safe" languages into OpenBSD? (2017)

Sorry if you interpreted my comment as advocating for tossing out grep or ls - that certainly wasn't what I was getting at. As one of the OpenBSD people that happens to quite like Rust, I'm aware of some of the other devs who have been using it, and we've talked a bit about Rust in general on occasion. The things that people appreciate about Rust tend to align with the things they like about OpenBSD, because the two projects have similar values. That's all I meant.

Mordak | 5 years ago | on: Re: Integrating "safe" languages into OpenBSD? (2017)

There are actually several of the OpenBSD devs that have been writing Rust lately. Some are doing it for work, some from personal interest. Opinions vary on how much people like it.

Rust and OpenBSD share a lot of technical values though so maybe this isn't surprising. Rust values safety and correctness. OpenBSD values safety and correctness. Rust values 'Just Works' with all the nice things cargo does and the whole 'fearless refactoring / concurrency' thing. OpenBSD values 'Just Works' with sane defaults and the 'batteries included' base system.

Mordak | 6 years ago | on: CBOR – Concise Binary Object Representation

FlatBuffers are not self-describing.

FlatBuffers, Protobuf, Cap'n Proto, etc., all require an external schema configuration that you compile into a code chunk that you include into your program. Without this it is impossible to make sense of the data. In our case, the data is semi-structured and changes frequently. The prospect of maintaining a schema registry for all the data users and keeping everyone up to date and backwards compatible is enough of a burden that it was excluded.

Avro also uses schemas, but since the schema is embedded in the file it is self-describing so the reader does not need to do anything special to interpret the data. But Avro's C library is buggy and the python deserialization performance was terrible, so Avro was not selected.

Mordak | 6 years ago | on: CBOR – Concise Binary Object Representation

At my work we recently went through a large exercise to decide on a common data storage format. The contenders were JSON, MessagePack, and Avro. MessagePack won because:

- Msgpack serialization and deserialization is very fast in many languages - often 100x faster then JSON

- Msgpack natively supports encoding binary data

- Msgpack has type extensions, making it trivial to represent common types in an efficient way (eg. IPv4 address, timestamps)

- Msgpack has good libraries available in many languages

If you do not care about those things (no binary data, no need for extended types, not performance critical) then JSON is just fine.

Mordak | 6 years ago | on: SCons: A Software Construction Tool

Please, no. My experience with the scons build cache is that it exists solely to introduce mysterious compile failures when the cache is out of sync. Eventually you get so distrustful of it that you end up just blowing away the cache as a matter of course.

Mordak | 6 years ago | on: SCons: A Software Construction Tool

I have exactly the opposite experience: I do not want to write a program to compile my program.

Having used both scons and waf, and also having used make, cmake and cargo, I would take any of the last three over scons or waf in a heartbeat. I think cargo is far and away the nicest to use because there is very little to actually do to build your program. Using waf or scons was always an exercise of 'I added a header file, so now I need to figure out the scons/waf API in order to tell it what to do with it'.

Mordak | 7 years ago | on: Secure CGI Applications in C on OpenBSD

pledge[1] allows a process to promise the kernel that it will restrict itself to a given subset of system calls. So you call pledge() with the set of syscalls you need, and then if your application does something else then it will be killed.

The OpenBSD httpd is privilege separated and chroot()ed, and each component only pledge()s the syscalls it needs. This reduces to just the syscalls needed to do filesystem operations inside the chroot, log, and talk on the internet. In the CGI context, if your application only needs to read / write to stdio to talk to httpd, then you can limit yourself to just stdio, or if it only needs to read files then it can limit itself to just those syscalls. If your application does something outside of your pledge() (eg. exec(), because it got pwned) then the kernel will kill it.

1. https://man.openbsd.org/pledge

Mordak | 7 years ago | on: OpenBSD 6.4 released

Keep reading. The upgrade guide has two parts: the first part that assumes the common configuration (you have console access and are using the OpenBSD boot loader), and the second part[1] in the event that you do not. If you have console access and can boot from iso, you can also use the cd64.iso image to boot into the install kernel and follow the common upgrade procedure on the console.

There is an in-between case where you have a console, but do not control the boot loader (so cannot boot bsd.rd) and cannot boot an iso. It sounds like maybe you are in this situation? In this case, you can still follow the upgrade directions as if you do not have a console[1]. Alternately, sometimes when I am in this situation I just download the install kernel (bsd.rd), move it where the boot loader is hard-coded to look (/bsd), and then reboot. The boot loader will boot the install kernel and you can follow the usual / common upgrade procedure on the console.

There is also autoinstall[2], which can automate the upgrade procedure for you and reduces upgrades to just rebooting into bsd.rd and waiting a bit. There is a bit of effort to create the response file, etc., so this may be overkill for a single instance but is very useful for upgrading fleets of machines quickly.

[1] https://www.openbsd.org/faq/upgrade64.html#NoInstKern [2] https://man.openbsd.org/autoinstall

Mordak | 7 years ago | on: Retguard: An improved stack protector for OpenBSD

It depends on the function. Many things will contribute. If your CPU can keep the cookie in cache then loading it repeatedly will relatively fast compared to hitting main memory. If your branch predictor can figure out the jmp over the int3 instructions quickly then that will also be fast. If the function is very short then the retguard stuff will add relatively more instructions to the function so will have a larger impact than if the function was long, etc.

I found that the runtime overhead was about 2% on average, but there are many factors that contribute.

Mordak | 7 years ago | on: Retguard: An improved stack protector for OpenBSD

Hey PaXTeam, thanks for having a look! I wrote the implementation, so I can answer some of these.

1. We don't silently skip instrumentation. If we can't find a free register then we will force the frame setup code to the front of the function so we can get one. See the diff in PEI::calculateSaveRestoreBlocks().

2. We do spill the calculated value to the stack. This is unavoidable in many cases (non-leaf functions). It would be an optimization to not do this in leaf functions, but this would also mean finding a register that is unused throughout the function. This turns out to be a small number of functions, so we didn't pursue it for the initial implementation.

3. I'm not sure what you mean by the cookies are shared. Do you just mean that they are all in the openbsd.randomdata section? They have to live somewhere. Being able to read arbitrary memory in the openbsd.randomdata section would leak them, yes, though this doesn't seem to have been a problem for the existing stack canary, which lives in the same section. I see that RAP keeps the cookie on a register, which sounds like a neat idea. I'd be curious to see how you manage to rotate the cookie arbitrarily.

4. I'm glad you like the int3 stuffing. :-) We could always make the int3 sled longer if it turns out these rets are still accessible in gadgets that terminate on the return instruction. Have you found any?

Anyway, I'm happy to see your commentary on this. You guys do some nice work! If you have other suggestions for improvement I'd be happy to hear them. You can email me at mortimer@.

Mordak | 8 years ago | on: Meltdown fix committed to OpenBSD

Wow, that's some seriously selective quoting from the krackattacks link there. The one that is relevant to the question of whether or not OpenBSD patched early without permission is this:

> As a compromise, I allowed them to silently patch the vulnerability.

It's pretty easy to argue that OpenBSD doesn't like embargoes, but it's pretty hard to argue that they ignore embargoes and patch whenever they want to. In this specific case, the project asked and received permission to patch early. The fact that the researcher regrets this in hindsight is beside the point.

Mordak | 8 years ago | on: Retguard: OpenBSD/Clang

Ah, Return Flow Guard is cool - I did not know that MS had done that!

I like SafeStack, but was disappointed to learn about the limitations with shared libraries. Some SafeStack is better than no SafeStack though, and it can probably be turned on without too much effort.

Mordak | 8 years ago | on: Retguard: OpenBSD/Clang

Oh, yes. There are other ways to kick off ROP chains that do not involve stack corruption. For these attacks, Retguard will only pollute the gadget space by inserting these return-address permuting instructions before some fraction of the c3 bytes in a program (a little under 50%, depending on the program).

Actually removing c2/c3 bytes and actively reducing the gadget space is a different endeavour. There has been a bunch of academic work in this regard, with varying levels of success. Some would say it is a fool's errand to try to remove all the ROP gadgets, but that's what fools are for. Stay tuned. :-)

Mordak | 8 years ago | on: Retguard: OpenBSD/Clang

I think the LLVM CFI options only apply to C++ programs?

Microsoft's CFG is cool, but works on the other end to this - by protecting CALL instructions instead of RET.

Stack cookies are similar, yes. This mechanism combines two ASLR-randomized values (the caller address and the stack address) to control program flow. Stack canaries use a constant random cookie per object file to detect stack smashing. Retguard raises the bar for successful ROP attacks, just as CFG raises the bar for function pointer corruption attacks.

Full disclosure, I am the author of the clang diff that kicked this off. The appeal of this mechanism is that it is cheap enough to apply in every function, pollutes the ROP gadget space with instructions that permute the next return address, and requires attackers to have more information in order to kick off ROP chains (the address where they are writing their ROP chain). I know about some of the other stuff people are working on (CPI seems promising), and look forward to turning those things on when possible. Meanwhile, this mitigation is cheap, localized inside every function, and doesn't require any program modification in the vast majority of situations (programs that inspect stack frames without consulting the unwind info may have problems), so is fairly easy to turn on everywhere.

Mordak | 8 years ago | on: Retguard: OpenBSD/Clang

You have it - if a stack address can be leaked, and you can follow the control flow to figure out the difference between the address you leaked and the address you're going to be dumping your ROP chain into, then you can just xor the gadget address with the stack address, and then do the math to xor any down-chain gadgets with the calculated stack address if the gadgets you want to use happen to have this xor instruction injected into them.

But you don't always have stack address leaks. Presently, in order to ROP you need (a) a leaked address to the space where your gadgets live and (b) the ability to write your ROP chain somewhere where the program will return into it. With this scheme, you now also need (c) the exact address where you are writing your ROP chain.

Not all info leak vulnerabilities leak arbitrary memory of the attacker's choosing. If they did, stack canaries would be pretty useless. So for those cases where a stack address leak is unavailable, this raises the bar against ROP.

page 1