diff --git a/src/chp1/about_the_team.md b/src/chp1/about_the_team.md index 7d41715..1ff3a7c 100644 --- a/src/chp1/about_the_team.md +++ b/src/chp1/about_the_team.md @@ -62,6 +62,7 @@ Listed alphabetically: * [tshepang](https://github.com/tshepang) * [U007D](https://github.com/U007D) * [Vishal Lama](https://github.com/vishallama) +* [w3irdrobot](https://github.com/w3irdrobot)
diff --git a/src/chp16_appendix/crypto.md b/src/chp16_appendix/crypto.md index 3f24b40..0c5b959 100644 --- a/src/chp16_appendix/crypto.md +++ b/src/chp16_appendix/crypto.md @@ -74,7 +74,7 @@ In practice, most implementations encrypt a byte (8 bits) at a time because mode By contrast, **block ciphers** need to break data up into fixed-size chunks. For the popular Advanced Encryption Standard (AES)[^AES], a block must be 128 bits (16 bytes). -While both stream and block ciphers accomplish the same goal, stream ciphers tend have a smaller memory footprint and faster runtimes[^Perf]. +While both stream and block ciphers accomplish the same goal, stream ciphers tend to have a smaller memory footprint and faster runtimes[^Perf]. So they're often used for low-resource embedded systems and real-time data processing. The "smarts" of any stream cipher algorithm is how it turns a finite-size key (RC4 lets Alice and Bob choose anywhere from 40 to 2,048 bits) into a **keystream** as long as the input data (which could be arbitrarily long, maybe we need to encrypt a 10 GB file). diff --git a/src/chp2/static_assurance_1.md b/src/chp2/static_assurance_1.md index 54d70f5..8e19286 100644 --- a/src/chp2/static_assurance_1.md +++ b/src/chp2/static_assurance_1.md @@ -59,7 +59,7 @@ Generally speaking: * *Failure to terminate* means the analysis never outputs a result. This can be due to "state explosion" - a combinatorial growth in complexity of the problem the analysis is trying to reason about. To avoid spinning forever, many commercial tools reduce complexity via approximation. Which, again, risks false positives. -Designing an static analysis algorithm practical enough to terminate (no state explosion) yet clever enough to never produce a false positive (no over-approximation) is, surprisingly often, impossible. +Designing a static analysis algorithm practical enough to terminate (no state explosion) yet clever enough to never produce a false positive (no over-approximation) is, surprisingly often, impossible. Not "impossible given our current knowledge and computational power". Provably impossible, as in the problem is mathematically *undecidable*[^AliasPaper][^AliasPaper2]. diff --git a/src/chp3/rust_5_own_2.md b/src/chp3/rust_5_own_2.md index a94aa68..eef8034 100644 --- a/src/chp3/rust_5_own_2.md +++ b/src/chp3/rust_5_own_2.md @@ -52,7 +52,7 @@ That's a solid starting point for high assurance software. > **Computers and Humans Exploring Software Security (CHESS)** > -> CHESS was DARPA research program[^CHESS] on "the effectiveness of enabling computers and humans to collaboratively reason over software artifacts...with the goal of finding 0-day vulnerabilities at a scale and speed appropriate for the complex software ecosystem upon which the U.S. Government, military, and economy depend"[^CHESSDesc]. +> CHESS was a DARPA research program[^CHESS] on "the effectiveness of enabling computers and humans to collaboratively reason over software artifacts...with the goal of finding 0-day vulnerabilities at a scale and speed appropriate for the complex software ecosystem upon which the U.S. Government, military, and economy depend"[^CHESSDesc]. > > It's a response to the fact that in-depth security assessments are a **difficult to scale expert process**. > Rust was not considered a solution under the CHESS program. diff --git a/src/chp3/rust_6_error.md b/src/chp3/rust_6_error.md index be833ce..0015cd7 100644 --- a/src/chp3/rust_6_error.md +++ b/src/chp3/rust_6_error.md @@ -201,7 +201,7 @@ Including implicit cases like this one. One goal of testing is to show that a program is robust enough to not hit such assertions in practice, due to checks and/or mitigations. Some number of fatal assertions will always be present, but thorough testing can give us confidence that a program avoids them. -Now in certain cases, we may be able to remove problem potential entirely. +Now in certain cases, we may be able to remove potential problems entirely. For example, we could have initialized the array using an iterator to eliminate the possibility of an out-of-bounds index: ```rust,noplaypen diff --git a/src/chp4/_index.md b/src/chp4/_index.md index 0b86529..da473aa 100644 --- a/src/chp4/_index.md +++ b/src/chp4/_index.md @@ -142,7 +142,7 @@ But after finishing the chapter, maybe you'll start viewing memory primarily thr * Develop a mental model of memory safety, type safety, and binary exploitation * Learn to debug Rust code using Mozilla `rr`[^RR] (an enhanced variant of `gdb`[^GDB]) * Understand how attackers exploit heap memory corruption bugs, step-by-step -* Write your first an introductory exploit or two, bypassing modern protections! +* Write an introductory exploit or two, bypassing modern protections! * Understand how Rust actually provides memory safety, including current limitations * Understand how modern, language-agnostic exploit mitigations work (and how they can fail) diff --git a/src/chp4/assure_stack_1.md b/src/chp4/assure_stack_1.md index 5c8ec34..99359ea 100644 --- a/src/chp4/assure_stack_1.md +++ b/src/chp4/assure_stack_1.md @@ -75,7 +75,7 @@ Lets visualize how a code snippet uses the stack, to make the push/pop discussio * We're interested in how this program uses stack memory at runtime, adding the attribute `#[inline(never)]` to ensure the compiler allocates a stack frame each time either `recursive_count_down` or `square` is called. - * "Inlining" is an opportunistic compiler optimization can avoids function call overhead, including stack frame allocation and caller-register preservation [^Inlining]. It's not always applicable and as programmer we don't directly decide where it is. So forgoing it is a realistic case to prepare for. + * "Inlining" is an opportunistic compiler optimization that avoids function call overhead, including stack frame allocation and caller-register preservation [^Inlining]. It's not always applicable and as programmer we don't directly decide where it is. So forgoing it is a realistic case to prepare for. If run with `cargo run -- 2`, this program outputs: diff --git a/src/chp4/attack_1.md b/src/chp4/attack_1.md index 179c38e..d46ecce 100644 --- a/src/chp4/attack_1.md +++ b/src/chp4/attack_1.md @@ -80,7 +80,7 @@ Be it C, C++, or `unsafe` Rust. For a more concrete discussion of memory and type safety, we'll examine three C snippets and visualize the violations therein. Note: -* None of the three snippets are, to the best our knowledge, *exploitable*. These small programs break safety, but not in manner unfortunate enough to enable a break of data-code isolation. +* None of the three snippets are, to the best our knowledge, *exploitable*. These small programs break safety, but not in a manner unfortunate enough to enable a break of data-code isolation. This distinction is intensional. We're starting by learning to identify bugs, even if innocuous. @@ -416,7 +416,7 @@ In this type safety example, it happens to be hardcoded and thus stored in stati > **What if we're not talking about binaries?** > -> We're focused on binary exploitation in this chapter, but that the data-is-code concept applies generally. +> We're focused on binary exploitation in this chapter, but the data-is-code concept applies generally. > Let's pick on Java for a moment. > > The Java language is ubiquitous in enterprise and shares its runtime with languages like Kotlin, Clojure, and Scala. diff --git a/src/chp4/sw_stack_1.md b/src/chp4/sw_stack_1.md index 54afd76..8c5d83b 100644 --- a/src/chp4/sw_stack_1.md +++ b/src/chp4/sw_stack_1.md @@ -69,7 +69,7 @@ Both topics are the subjects of entire technical books, so we'll visually diagra

Main memory, a physical machine's Random Access Memory (RAM), supports all non-trivial runtime computation. -The bit-patterns it stores and operates on representations two distinct items: +The bit-patterns it stores and operates on represent two distinct items: * **Data** - Variable-length sequences of bytes representing any information: hardcoded strings, colors codes, numbers, entire image and video files, etc. Each byte can be addressed individually, even if word-aligned accesses are often preferable for performance. @@ -114,7 +114,7 @@ Fortunately, we don't have to consider or understand such minutia when programmi The jobs of various registers are, by contrast, important for a working mental model. In addition to the IP, the two special purpose registers worth noting are: -* The **Stack Pointer (`SP`)** register - the address denoting the bottom of the current stack frame. A stack frame is akin to function's in-RAM "notepad" for computing and saving *function-local* results. +* The **Stack Pointer (`SP`)** register - the address denoting the bottom of the current stack frame. A stack frame is akin to a function's in-RAM "notepad" for computing and saving *function-local* results. * In the statement `let x = 3 + 6;`, `x` will be computed using registers, then the value `9` will be stored on the stack[^RegisterAlloc]. This allows the CPU to re-use its small, fixed set of `GP*` registers for new computations when multiple functions are called in a program. @@ -185,7 +185,7 @@ It's important you commit it to memory, pun intended. > The kernel runs in "ring 0", the most privileged mode available. > It can read/write from/to any processes's memory, directly access hardware, and control special CPU features. > -> Rings 2 and 3 are almost never used in practice[^RingProt]. +> Rings 1 and 2 are almost never used in practice[^RingProt]. > These modes were intended for device drivers, special programs that allow a kernel to communicate with vendor-specific hardware. > In reality most device drivers are loaded directly into the kernel, running alongside it in ring 0 (creating a major OS attack surface[^MSKernBlocklist]). >