sarah – the evolving ultrasaurus Sarah Allen's reflections on internet software and other topics Sat, 04 Jul 2020 10:47:06 +0000 en-US hourly 1 https://wordpress.org/?v=5.7.1 hope is not a strategy /2020/07/hope-is-not-a-strategy/ /2020/07/hope-is-not-a-strategy/#respond Sat, 04 Jul 2020 10:46:13 +0000 /?p=7019 Continue reading ]]> “Yeah I think I got to the limit of my bargaining … getting to end of May, and when I didn’t seem to get past my last best day which was 2 weeks ago” It was May 25th, the day I remember grieving for my lost life, text chatting with my flu buddy about the five stages of grief: denial, anger, bargaining, depression and acceptance.

“I think the thing is that I have to learn to live with this — I can’t just keep expecting, waiting to suddenly get better.” I stopped making plans beyond “rest, walk a little, rest, eat a little, rest…” in achingly slow motion, on a good day I could send an email and talk on the phone to a friend.

It’s not that I gave up hope, it’s just that thinking about any specific hope for the future was just not helpful. Recently, my brother shared a poem that captured this experience well:

My grandmother once gave me a tip:
In difficult times, you move forward in small steps.
Do what you have to do, but little by little.
Don’t think about the future, or what may happen tomorrow.
Wash the dishes.
Remove the dust.
Write a letter.
Make a soup.
You see?
You are advancing step by step.
Take a step and stop.
Rest a little.
Praise yourself.
Take another step.
Then another.
You won’t notice, but your steps will grow more and more.
And the time will come when you can think about the future without crying.

— Elena Mikhalkova, The Room of Ancient Keys

I couldn’t find anything else written by Elena Mikhalkova that has been translated into English. I’d love to hear about it if her books or any of her other poems is ever translated.

]]>
/2020/07/hope-is-not-a-strategy/feed/ 0
duck typing in rust: trait vs type /2020/01/duck-typing-in-rust-trait-vs-type/ /2020/01/duck-typing-in-rust-trait-vs-type/#respond Sat, 25 Jan 2020 22:33:23 +0000 /?p=7004 Continue reading ]]> A Trait in the Rust programming language enables what today’s coders commonly call “duck-typing” (walks like a duck and quacks like a duck).

In Rust, type refers to concrete types — the type of a value; whereas, a Trait refers to an abstract or generic type. Here the English word type lacks the specificity we need to describe these concepts, so we need adjectives to differentiate.

TLDR: traits vs types

The type keyword lets us define a type alias, like:

type Population = i32;

This is useful if I’m often passing around specific variables for a Population, and I have function that takes multiple numbers, then the compiler will be able to catch certain classes of errors:

fn report(p: Population, num: i32)

A Trait is a collection of functions that can be applied to a type (a built-in type, like i32 or a type that we have defined with a struct, enum or type alias). A good example of a Trait is ToString which is part of the Rust standard library:

pub trait ToString {
    fn to_string(&self) -> String;
}

Here’s a naive approach to implementing ToString on a custom struct:

struct Monster {
  eyeball_count: i32,
}

impl ToString for Monster {
  fn to_string(&self) -> String {
    format!("{}-eyed {}", self.eyeball_count, "monster")
  }
}

fn main() {
  let m = Monster { eyeball_count: 3 };
  println!("We just created a {}!", m.to_string())
}

Experienced Rustaceans would rarely implement the above code, instead they might implement std::fmt::Display which provides additional functionality we probably want, so if I write this instead of impl ToString for Monster:

impl fmt::Display for Monster {
  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    write!(f, "{}-eyed {}", self.eyeball_count, "monster")
  }
}

Then our code that calls to_string will still work, but we can also provide the variable directly to println! (or format!) and it will also work:

  println!("We just created a {}!", m);

This is because Rust allows for implementing functions on Traits in terms of a generic type.

Traits in context

Traits help break down what’s needed and make our code reusable and easier to maintain. Most libraries will use many Traits from the std libary, as well as their own Traits, which can make the learning curve a bit steep, but very flexible once you understand what they do.

So, as I’m learning, it helps me to spell everything out, but then I end up with code that was a bit hard to read. Check out this beauty that I wrote yesterday:

async fn read_some<R: AsyncRead + Send + Unpin>(mut reader: R) -> Result<(), std::io::Error>

I’m using Rust 1.39 with built-in async fn which means that the compiler will build me a future and I can declare as the return value to be whatever type that Future will ultimately produce (or simply what my function returns). In this case, I want to be able to pass in a tokio::net::TcpStream and also a “ReadHalf” that is returned from split.

My first attempt at refactoring was to do this:

type Reader = AsyncRead + Send + Unpin;

The above code doesn’t do what I wanted. I’ve explained above that type creates an alias for a concrete type; however, when we provide Traits, it (unexpected for me) creates a “Trait object” which is not the abstract type I was looking for. What I wanted to do was to define a new Trait that composes the other traits, but has no implementation of its own. Here’s the syntax I was looking for:

trait Reader: AsyncRead + Send + Unpin { } 
impl<T: AsyncRead + Send + Unpin> Reader for T {}

which I can then use like this:

async fn read_some<R: Reader>(mut reader: R) -> Result<(), std::io::Error>

or in a slightly more readable form with where:

async fn read_some<R>(mut reader: R) -> Result<(), std::io::Error>
where R: AsyncReader  

If you want to see the working code, I have a few examples here: https://github.com/ultrasaurus/rust-tokio-proxy where each can be executed like this: cargo run --example reader-type


Many thanks to the Alice, David, Lucio and Jeb on Tokio discord who helped me understand types, traits and how they are used in tokio!

]]>
/2020/01/duck-typing-in-rust-trait-vs-type/feed/ 0
writing c library in rust /2020/01/writing-c-library-in-rust/ /2020/01/writing-c-library-in-rust/#respond Thu, 16 Jan 2020 06:36:12 +0000 /?p=6995 Continue reading ]]> I want to write a library in Rust that can be called from C and just as easily called from Rust code. The tooling makes it pretty easy, but I had to look in a few places to figure how it is supposed to work and get tests running in both languages.

C library

To focus on the process of building and testing, the library will have a single function that adds two numbers. I wrote it in pure C first:

lib.c

int add(int a, int b) {
  return a + b;
}

lib.h

int add(int a, int b);

main.c

#include <stdio.h>
#include "lib.h"

int main() {
    int sum = add(1,2);
    printf("1 + 2 = %d\n", sum);
}

one-liner to compile and run the app

gcc *.c -o app && ./app

output: 1 + 2 = 3

then I wrote a simple automated test, based on tdd blog post

Rust library

cargo new add --lib

replace lib.rs with

#[no_mangle]
pub extern "C" fn add(a: i32, b:i32) -> i32 {
    a + b
}


#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        use crate::add;
        assert_eq!(add(2,2), 4);
    }
}

build and run with cargo test
which should have output like

$ cargo test
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running target/debug/deps/add-45abb08ccefdc53c

running 1 test
test tests::it_works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

   Doc-tests add

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

Compile as static library

Add to Cargo.toml

[lib]
name = "add"
crate-type = ["staticlib"]  

Now cargo build generates a compiled library file: target/debug/libadd.a. I could have stopped there, but I expect to iterate on this a bit and I had read about a crate that generates the C header file…

Generate a header (command-line)

First, install the lovely cbindgen crate (using –force just to make sure everything is up to date with the latest):

cargo install --force cbindgen

the command-line tool is pretty neat:

touch cbindgen.toml    # can be empty for defaults
cbindgen --config cbindgen.toml --crate add --output add.h

The above command will generate “add.h” file at the root of the crate.

Generate a header (cargo build)

I prefer to have the header generation integrated with cargo build (or at least I think I will). Here are the steps:

Add to Cargo.toml:

[build-dependencies]
cbindgen = "0.12"

By default, the header file is buried in target/debug/build/add-... with a bunch of intermediary build files. I find that it is nice to put it at the root of my crate where it is easy to find. Below is a custom build file that puts it in the crate root (aka CARGO_MANIFEST_DIR, the directory that Cargo.toml is in).

build.rs:

extern crate cbindgen;

use std::env;
use std::path::PathBuf;


fn main() {
  let crate_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR")
        .expect("CARGO_MANIFEST_DIR env var is not defined"));

  let config = cbindgen::Config::from_file("cbindgen.toml")
        .expect("Unable to find cbindgen.toml configuration file");

  cbindgen::generate_with_config(&crate_dir, config)
        .expect("Unable to generate bindings")
        .write_to_file(crate_dir.join("add.h"));
}

As mentioned above, cbindgen.toml may be empty, but here’s some settings I like:

include_guard = "add_h"
autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
language = "C"
includes = []
sys_includes = ["stdint.h"]
no_includes = true   

Confusingly no_includes means no extra includes. I prefer to have only the ones that I know are needed, rather than some random list of “common” headers.

Here’s my generated header:

#ifndef add_h
#define add_h

/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */

#include <stdint.h>

int32_t add(int32_t a, int32_t b);

#endif /* add_h */

Putting it all together

Example main.c in the root of the crate:

#include <stdio.h>
#include "add.h"

int main() {
    int sum = add(1,2);
    printf("1 + 2 = %d\n", sum);
}

compile and run:

gcc main.c add/target/debug/libadd.a -o app && ./app

outputs: 1 + 2 = 3

This make me unreasonably happy. Rust syntax can be a bit finicky and certainly takes a bit of getting used, but this kind of tooling could more than make up for that in accelerating the dev cycle.

For the full applications with a mini test suite in C, see github/ultrasaurus/rust-clib — the v0.1 branch is from when this blog post was written.

]]>
/2020/01/writing-c-library-in-rust/feed/ 0
rust on heroku with hyper http /2020/01/rust-on-heroku-with-hyper-http/ /2020/01/rust-on-heroku-with-hyper-http/#respond Mon, 06 Jan 2020 02:39:54 +0000 /?p=6984 Continue reading ]]> Running an app on heroku requires at least one entry point responding to http. An easy way to do this is to use hyper to create a simple web service.

Setup

cargo new hello_rust --bin
cd hello_rust
git init
git add .
git commit -m "cargo new hello_rust --bin"

add to Cargo.toml:

[dependencies]
hyper = "0.13"

Web service code

The core code to set up a little web service has a few key parts:

  1. the service (async fn hello)
    • an async function that takes a hyper::Request and returns a hyper::Response in the Result
    • Request is generic over the Body, so it seems nifty to be able to provide our own Rust types for specific content (like JSON) and also for validating API POST params
    • Result is Infallible: a Rust error type signifying that the function never returns an error
  2. make_service_fn — docs are a bit sparse on this, but I think all it does it generate an instance of the service with the Request context that so that each request can run concurrently
  3. Server::bind(&addr).serve(…) — Hyper uses a builder pattern where Server::bind generates a Builder, where you can configure http1/2 support and then a running instance of the Server is created by calling the serve method with the service function.

To see this in action, replace main.rs with this code:

use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Request, Response, Server};
use std::convert::Infallible;

async fn hello(_req: Request<Body>) -> Result<Response<Body>, Infallible> {
    Ok(Response::new(Body::from(
        "<HTML><H1>Hello World!</H1><HTML>",
    )))
}

#[tokio::main]
pub async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {

    let make_svc = make_service_fn(|_conn| {
        async { Ok::<_, Infallible>(service_fn(hello)) }
    });

    let addr = ([0, 0, 0, 0], 3000).into();

    let server = Server::bind(&addr).serve(make_svc);

    println!("Listening on {}", addr);

    server.await?;

    Ok(())
}

to run the app locally:

cargo run

then in the browser, go to http://localhost:3000/

Heroku setup

1. Heroku CLI

Install heroku CLI or if you already have it:

heroku update

Then to set up the app on heroku:

heroku create --buildpack emk/rust

2. Procfile

Add a Procfile for heroku to know what to call when it receives a web request:

echo "web: ./target/release/hello_rust" >> Procfile

3. Port configuration

Heroku requires that we listen on the port specified with the PORT env var. So, add the following code and replace the hard-coded port number with this variable:

    let port = env::var("PORT")
        .unwrap_or_else(|_| "3000".to_string())
        .parse()
        .expect("PORT must be a number");

4. Deploy!

Deploy the app by pushing code to the Heroku remote repository that was set up by the CLI in step 1.

 git push heroku master

Full code for the app is on github.com/ultrasaurus/hello-heroku-rust

Background

My environment info (rustup show):

stable-x86_64-apple-darwin (default)
rustc 1.39.0 (4560ea788 2019-11-04)
]]>
/2020/01/rust-on-heroku-with-hyper-http/feed/ 0
rust on heroku with async/await and tokio /2020/01/rust-on-heroku-with-async-await-and-tokio/ /2020/01/rust-on-heroku-with-async-await-and-tokio/#respond Sun, 05 Jan 2020 04:27:25 +0000 /?p=6975 Continue reading ]]> In an effort to understand the new Rust async/await syntax, I made a super-simple app that simply responds to all HTTP requests with Hello! and deployed on Heroku.

Update: If you just want to create a webservice in Rust and deploy on Heroku, I recommend next blog post: rust on heroku with hyper http. This blog post focuses on the details of how the underlying request and response is handled with async/await, on stable Rust since 11/2019.

The full source code and README instructions can be found on github.com/ultrasaurus/hello-heroku-rust, tokio-only branch

Rust “hello world” app

Make a new project with cargo

cargo new hello_rust --bin
cd hello_rust
git init
git add .
git commit -m “cargo new hello_rust —bin”

cargo run

output:

   Compiling hello_rust v0.1.0 (/Users/sallen/src/rust/hello_rust)
    Finished dev [unoptimized + debuginfo] target(s) in 1.47s
     Running `target/debug/hello_rust`
Hello, world!

Heroku setup

Rust isn’t officially supported by Heroku yet, but there are lots of “buildpacks” which help to deploy a Rust app. I picked emk/heroku-buildpack-rust — most stars, most forks & recently updated!

We need the heroku CLI. I already had it and just did heroku update to sync to latest version (7.35.1). Then to set up the app on heroku:

heroku create --buildpack emk/rust

output provides a unique hostname by default:

Creating app... done, ⬢ peaceful-gorge-05620
Setting buildpack to emk/rust... done
https://peaceful-gorge-05620.herokuapp.com/ | https://git.heroku.com/peaceful-gorge-05620.git

We need a Procfile so heroku knows our entrypoint

echo "web: ./target/release/hello_rust" >> Procfile

Write the app

Add crate dependencies to Cargo.toml and add code to main.rs (and other files as with any Rust app). The emk/rust buildpack takes care of building everything as part of the heroku deploy.

The following lines (in Cargo.toml) will add all of tokio features:

[dependencies]
tokio = { version = "0.2", features = ["full"] }

I’d rather specify only what’s needed, but ran into something I couldn’t debug myself (issue#2050)

The core of the app accepts the sockets connections, but doesn’t read/write:

use std::env;
use tokio::net::TcpListener;

#[tokio::main]
async fn main() {
    // Get the port number to listen on (required for heroku deployment).
    let port = env::var("PORT").unwrap_or_else(|_| "1234".to_string());

    let addr = format!("0.0.0.0:{}", port);
    let mut listener = TcpListener::bind(addr).await.unwrap();

    loop {
        println!("listening on port {}...", port);
        let result = listener.accept().await;
        match result {
            Err(e) => println!("listen.accept() failed, err: {:?}", e),
            Ok(listen) => {
                let (socket, addr) = listen;
                println!("socket connection accepted, {}", addr);
                println!("not doing anything yet");
            }
        }
    }
}

Deploy on heroku

The above code will build and deploy, by simply pushing the code to heroku:

heroku push origin master

We can see what it is doing with heroku logs --tail:

Here’s where it starts the build and then kills the old app:

2020-01-05T03:45:31.000000+00:00 app[api]: Build started by user ...
2020-01-05T03:45:50.450898+00:00 heroku[web.1]: Restarting
2020-01-05T03:45:50.454311+00:00 heroku[web.1]: State changed from up to starting
2020-01-05T03:45:50.244579+00:00 app[api]: Deploy 399e1c85 by user ...
2020-01-05T03:45:50.244579+00:00 app[api]: Release v24 created by user ...
2020-01-05T03:45:50.701533+00:00 heroku[web.1]: Starting process with command `./target/release/hello_rust`
2020-01-05T03:45:51.741040+00:00 heroku[web.1]: Stopping all processes with SIGTERM
2020-01-05T03:45:51.819864+00:00 heroku[web.1]: Process exited with status 143

Oddly, it seems to start the app before “State changed from starting to up” but it will fail if we’re not listening on the right port, so maybe that is as expected:

2020-01-05T03:45:52.343368+00:00 app[web.1]: listening on port 49517...
2020-01-05T03:45:53.322238+00:00 heroku[web.1]: State changed from starting to up
2020-01-05T03:45:53.303486+00:00 app[web.1]: socket connection accepted, 10.171.202.59:17201
2020-01-05T03:45:53.303545+00:00 app[web.1]: not doing anything yet
2020-01-05T03:45:53.303619+00:00 app[web.1]: listening on port 49517...
2020-01-05T03:45:53.313259+00:00 app[web.1]: socket connection accepted, 172.17.146.217:43686
2020-01-05T03:45:53.313285+00:00 app[web.1]: not doing anything yet
2020-01-05T03:45:53.313370+00:00 app[web.1]: listening on port 49517...
2020-01-05T03:46:28.000000+00:00 app[api]: Build succeeded
2020-01-05T03:46:48.251168+00:00 heroku[router]: at=error code=H13 desc="Connection closed without response" method=GET path="/" host=peaceful-gorge-05620.herokuapp.com request_id=a0d630d9-790a-47db-87af-67e680b27907 fwd="69.181.194.59" dyno=web.1 connect=1ms service=1ms status=503 bytes=0 protocol=https

So, the first socket connection above is some internal heroku checker, then when I attempt to go to the app URL in the browser, it fails (as expected).

Async read and write

I tried to keep the code clear with as little magic as possible. It’s a bit verbose (without even handling HTTP in any general way), but I found it helpful to see the details of read and write.

Note that adding use tokio::prelude::*; allows calling of read_line (defined in tokio::io::AsyncBufReadExt) and write_all (defined in tokio::io::AsyncWriteExt).
The additional code reads the bytes from the socket line by line until we get the the end of the HTTP Request (signalled by a blank line). So we look for two CLRFs (one at the end of the last header line and one for the blank line).

tokio::spawn(async move makes it so sure we can read/write from one socket while also listening for additional connections. tokio::spawn will allow the program execution to continue, while concurrently allowing our async function process_socket to read and write from the socket. Because we added #[tokio::main] above our async fn main entry point, tokio will set up an executor which will wait for all of our spawned tasks to complete before exiting.

use std::env;
use tokio::net::TcpListener;
use tokio::prelude::*;

#[tokio::main]
async fn main() {
    // Get the port number to listen on (required for heroku deployment).
    let port = env::var("PORT").unwrap_or_else(|_| "1234".to_string());

    let addr = format!("0.0.0.0:{}", port);
    let mut listener = TcpListener::bind(addr).await.unwrap();

    loop {
        println!("listening on port {}...", port);
        let result = listener.accept().await;
        match result {
            Err(e) => println!("listen.accept() failed, err: {:?}", e),
            Ok(listen) => {
                let (socket, addr) = listen;
                println!("socket connection accepted, {}", addr);
                // Process each socket concurrently.
                tokio::spawn(async move {
                    let mut buffed_socket = tokio::io::BufReader::new(socket);
                    let mut request = String::new();
                    let mut result;
                    loop {
                        result = buffed_socket.read_line(&mut request).await;
                        if let Ok(num_bytes) = result {
                            if num_bytes > 0 && request.len() >= 4 {
                                let end_chars = &request[request.len() - 4..];
                                if end_chars == "\r\n\r\n" {
                                    break;
                                };
                            }
                        }
                    }
                    if let Err(e) = result {
                        println!("failed to read from socket, err: {}", e);
                        return;
                    }
                    let html = "<h1>Hello!</h1>";
                    println!("request: {}", request);
                    let response = format!(
                        "HTTP/1.1 200\r\nContent-Length: {}\r\n\r\n{}",
                        html.len(),
                        html
                    );
                    let write_result = buffed_socket.write_all(response.as_bytes()).await;
                    if let Err(e) = write_result {
                        println!("failed to write, err: {}", e);
                    }
                });
            }
        }
    }
}

Background

Here’s my environment info (rustup show):

stable-x86_64-apple-darwin (default)
rustc 1.39.0 (4560ea788 2019-11-04)

Reference docs

  • https://docs.rs/tokio/0.2.6/tokio/net/struct.TcpListener.html
  • https://docs.rs/tokio/0.2.6/tokio/net/struct.TcpStream.html
  • https://docs.rs/tokio/0.2.6/tokio/task/fn.spawn.html
]]>
/2020/01/rust-on-heroku-with-async-await-and-tokio/feed/ 0
patterns of actions are a making /2019/12/patterns-of-actions-are-a-making/ /2019/12/patterns-of-actions-are-a-making/#respond Tue, 24 Dec 2019 20:27:17 +0000 /?p=6961 Continue reading ]]> “There are things that you don’t even realize that you can do.” In a recent podcast, B. Mure tells about graphic facilitation:

I didn’t really know it was a skill to have: to listen to people and very immediately draw something related to what they were talking about and present their ideas in a visual way.

Sometimes this is also called making “sketch notes.” He goes on to talk about a general phenomenon where you discover that you can do a thing that you never thought was a thing:

There are so many things that, if you are not given the opportunity, if somebody doesn’t see that within you, or thinks maybe you should just come along and try this thing that I’ve organized, there are things that you don’t even realize that you can do.

I think there are two parts of this that are transformational, that I’ve experienced myself and occasionally been able to spark in others.

  1. Naming a pattern of actions or behavior: this is an act of creating that is rare and powerful. Discovering something is a skill that one can be good at and apply with intention is aided by that skill having a name.
  2. Recognizing the spark within someone else: seeing a capability, especially latent potential, within someone and naming it, inviting that person to experiment with a new skill, encouraging creative action in the world.

There should be a word for the making of words that is more than coining a term, where the naming of a thing helps people do (or not do) whatever that is.

I’ve thought a lot about how the term mansplaining has helped a generation notice and often modify behavior. I learned about restorative justice as a framework for transforming guilt into responsibility. I remember when, at an early RailsBridge workshop, I applied lessons from my kid’s preschool to the challenge of how to teach without grabbing the keyboard (“Use your words”).

I love the idea of a word for a skill providing a path to developing that skill, connecting to a community, and finding paid work.


The whole podcast with Dan Berry and B. Mure is worth listening to for any creative folk or if you enjoy comics or visual storytelling and want a glimpse of that world.

Image from website of B. Mure: the artist in sunshine looking skyward with arms spread wide upward in front of mountains and blue sky
https://bmuredraws.com/

]]>
/2019/12/patterns-of-actions-are-a-making/feed/ 0
memory safety: necessary, not sufficient /2019/12/memory-safety-necessary-not-sufficient/ /2019/12/memory-safety-necessary-not-sufficient/#respond Sun, 22 Dec 2019 22:33:28 +0000 /?p=6933 Continue reading ]]> As I think about developing new Internet-connected software, I worry about the safety of the people who use it. By 2021, most Web browsers won’t allow native code extensions, which will eliminate a lot of potential issues, while a hug swath of creative animations and interactives will disappear from the Web. I spent some time this summer, thinking about what I could learn from the security vulnerabilities in the Flash Player that has been much maligned in recent years.

Flash CVEs (2001-2009)

I looked at the Common Vulnerabilities and Exposure List (CVE List hosted by Mitre with all reports 2001-2019. I found 1172 Flash Player vulnerabilities, which sounds huge, but in context of vulnerabilities reported in Web Browsers, doesn’t look that bad:

  • 1172 Flash Player
  • 1999 Internet Explorer
  • 2033 Chrome
  • 2442 Firefox

Note: these numbers don’t necessarily tell us that Firefox had more vulnerabilities than Internet Explorer. It could mean that Firefox was more rigorously open in reporting vulnerabilities, which seems likely.

Understanding attack vectors

Vulnerabilities in the Flash Player were particularly dangerous because Flash was installed on all of the Web Browsers, so any flaw in Flash was much easier to exploit than a flaw in a specific Web Browser. To understand this, one needs to understand that the primary attack vector enabling a hacker to take advantage of a vulnerability in Flash Player was to create a malicious Flash application or movie that would distract the user while doing something illicit or intentionally trigger a crash and then exploiting that crash to execute native code with access to the user’s machine.

In the larger context of a specific attack, a vulnerability in the Flash Player would typically need to be combined with something else:
* deceptive emails (aka phishing)
* deceptive websites
* “man in the middle” attacks (replace real Web content with malicious content that appears identical)

Categorizing vulnerabilities

I conducted a rough cut analysis of matching terms by reading the list of CVEs and creating categories that might provide instructive value in thinking through how to avoid similar issues in the future.

pie chart illustrating data in table below

  • 802 memory safety
  • 42 other code execution
  • 58 XSS, CORS, CLRF
  • 61 parsing / validation
  • 13 clickjacking
  • 91 bypass sandbox
  • 105 other

Memory safety (~70%)

The vast majority of issues (“memory safety”) resulted from coding errors, which can now be avoided with modern programming languages. For a long time, we’ve been able to use languages like Erlang/Elixir, Java, Python, Ruby, and Go for server-side coding with memory safety features. Even C++ has language features and libraries (though you must choose to use them). Now, for low-profile client software we can use Rust or WebAssembly when we need something higher performance or less memory-hungry than JavaScript.

Escaping the “sandbox” (~15%)

If we develop code that runs in a Web browser, we can trust the browser’s “sandbox” — our apps can only use a restricted set of APIs. If we’re writing a Web browser or any other Internet-connected software used by humans or machines, then it is a good idea to carefully isolate our code that can access the operating system to write files or make network calls.

From my CVE analysis, coding errors in this category resulted in just over 15% of CVEs (other code execution, bypassing sandbox, and XSS, CORS, CLRF issues). Of course, the biggest thing you can do is not include the code that does powerful things you don’t want to allow. However, sometimes you do need to load and execute a shared library, accessing the filesystem and the network.

Parsing / Validation (~5% / ~15% excluding memory safety)

Parsing and validation of input (mostly reading a file or parameter) is another common coding error pattern which can result in a serious vulnerability. Having to fix these kinds of issues causes me to be very careful when adding parsing code to any app or library. If we exclude memory safety errors, parsing and validation errors are larger than any identified class of error.

pie chart excluding memory safety shows 16% parsing / validation

Clickjacking and “other”

Clickjacking is noteworthy for anyone developing a client app with extensions where 3rd party developers (or other users via content sharing) can present information to the user and allow interaction. This class of attack uses features that are designed to empower users to present compelling content to be instead used to trick people into doing something unintended. For example, there were bugs that allowed Flash content to overlay other web pages or browser UI, thereby tricking the user into clicking or typing in a way to provide privileged access.

Perhaps “other” deserves a closer look, but I didn’t find clear patterns and suspect that contains many smaller categories.

Parsing is hard

In my experience, many programmers recognize that implementing an extension mechanism that allows for user interaction or providing a “sandbox” for 3rd party code can be very tricky to get right and will exercise great care in writing or using that kind of code. However, I have often interacted with programmers who don’t seem to believe that writing code to parse text is difficult. Writing code that performs the intended action is not hard, but writing code that has no unintended effects requires very careful coding and a little imagination.

Looking toward open source code for some examples to learn from, here are a few examples of URL parsing libraries where bugs were found (and fixed) after vulnerabilities were discovered in the field:

  • https://github.com/envoyproxy/envoy/issues/7728 (Envoy proxy)
  • https://go-review.googlesource.com/c/go/+/189258/ (Go)
  • https://www.cvedetails.com/cve/CVE-2018-3774/ (url-parse Node library)

The results of this analysis were included as part of Code Mesh LDN 19 talk, A landscape of unintended consequences (video, slides). The data and methodology is available at on github: ultrasaurus/flash-cve-analysis.

]]>
/2019/12/memory-safety-necessary-not-sufficient/feed/ 0
brief history of rtmp + future thoughts /2019/12/brief-history-of-rtmp-future-thoughts/ /2019/12/brief-history-of-rtmp-future-thoughts/#respond Tue, 17 Dec 2019 01:47:02 +0000 /?p=6922 Continue reading ]]> RTMP: web video innovation or Web 1.0 hack… how did we get to now? (Demuxed 2019)

It was fun to go back in time and recall why Flash was great in 2000, when IE 5.5 had just been released and you couldn’t rely on CSS actually working. In prepping for this talk, I worked really hard to try to express what Web development was like then and why people loved Flash: “200K of cross-platform goodness.” Flash made the Web work for high fidelity interactive graphics 20 years ago, which I think helped drive Web standards to support more than text, images and links.

“We wanted to support all the people on the internet.” It still boggles my mind how we could support low-latency way-back-then and now when computers and networks are faster it seems impossible… sometimes I try to visualize what is happening to the bits as I wait for something to happen. (I really do know why things are slower now, and it’s not just about the tech, but that’s a different story.)

HTTP tunneling worked much better than you would expect… sometimes it’s good to make something work for everyone, even if not optimal.

I fondly remembering Doug Engelbart telling me that his Augment system (built in the 1960s) had more features than Flash Player 6 + Flash Communication Server in 2002. (It’s great when your heroes tell you that your great accomplishments are not all that interesting.) Later, he did acknowledge that making this stuff available to everyone on the Web was “pretty good.” Inspiring widespread adoption, creating an ecosystem is a different kind of innovation.

The thing that makes it an ecosystem is that each essential component can be bought from multiple companies and is available as open source. At first Flash was essential, now much later, Flash doesn’t really matter anymore to the relevance of the RTMP protocol.

Today there are 500M IP camera on the Internet, about the number of people on the Web when Flash Player 6 was released. SmartHome video sensors have insane growth. The future of video is not about how to catch up with latency and resolution of live broadcast TV (though that will happen), it’s about how we can be integrate video streams from new devices, how we can help the machines help the people by creating new applications. Maybe RTMP will be a part of that, what do you think?

]]>
/2019/12/brief-history-of-rtmp-future-thoughts/feed/ 0
rust 2020: fulfill the promise /2019/12/rust-2020/ /2019/12/rust-2020/#respond Sun, 01 Dec 2019 09:37:16 +0000 /?p=6885 Continue reading ]]> As a newcomer to Rust, my suggestion for 2020 theme is to fulfill the promise of “empowering everyone to build reliable and efficient software” by finishing what’s started (rather than adding new features), continuing the focus on good docs and good tools, and expanding to develop a coherent ecosystem.

Rust empowers you to reach farther, to program with confidence in a wider variety of domains than you did before. — Rust Language Book forward

Overview themes

2020 roadmap: finish what’s started, fulfill the promise

2021 edition: scalability – Can newcomers to Rust create a real-world, complex system without recreating basic components or contributing to the language itself?

  • more scalable systems written in Rust
  • experienced C/C++ engineers can easily transition to Rust
  • more scalable ecosystem
    • commonly needed libraries are available
    • new engineers can easily become contributors

Keep doing

  • Tooling is great! rustup toolchain, feature flags, online/offline docs make it easy to experiment with new Rust/crate features, even as a relatively new Rust programmer.
  • Transparency (like this call for blog posts, RFC process including roadmap)
  • Focus on good docs & good error reporting is incredibly helpful. Keep iterating on this!

Feature requests

  • safety beyond memory safety and concurrency. For example: URL parsing should be in std have a shared implementation that supports common use cases — it’s risky for Internet apps to not have a stable, well vetted URL parser, why are there three? (That’s a rhetorical question. I know why, but don’t think there need to be. See twitter discussion [Update 12/16/2019: I’d like to see convergence in URL parsers (or perhaps a shared common library) the way the community seems to be converging around serde.]
  • async all the things! I think this is already the plan. I look forward to async I/O (network and files) to be supported in the std library. I appreciate the thoughtfulness about safety, factoring out useful core concepts (like Pin/Task), and ensuring compatibility with Futures and Tokio crates. Consider other async use cases: GPU, OpenGL
  • lifetimes visualization would accelerate learning curve on resolving compiler errors, good ideas in this thread

Slow down to speed up

In my experience writing documentation often uncovers design issues and bugs. RFC template has a guide-level explanation section, which is great, and taking that one step further to writing baseline docs before declaring a feature “stable” would create positive pressure for community focus. Some ideas for process improvements…

  • A crisp “definition of done” could help focus the community. Consider adding requirements to releasing ‘stable’
    • RFC updated to reflect what was completed and is still open
    • stable reference docs are complete or include link to RFC
  • Consider WIP limits: how limiting work-in-progress increases speed

It seems in keep with Rust values to create a strong incentive to support contributing writers who are working to take the feature over-the-line and encourage new engineers to contribute. It is easier for new contributors to work with APIs that are documented or clearly dive into a work-in-progress, aware that they are contributing to finishing something.

Other Improvements

  • Documentation shouldn’t require deep knowledge of Rust (example: https://stackoverflow.com/questions/56402818/how-do-i-interpret-the-signature-of-read-until-and-what-is-asyncread-bufread-i/56403568#56403568)

Background

The reason I’m learning Rust is that I am experienced engineer with a need to write performant, low-profile client/server code. I’m excited about the idea of writing one body of code that can (potentially) work across native desktop, mobile, servers… and with cross-compilation to WebAssembly (Wasm), also browsers and edge servers.

Arguably, C works for all my needs, it even cross-compiles to Wasm. I want to like Rust better. I do in theory, but in practice, it’s got a lot of sharp edges (which is saying a lot when comparing it to C).


Updated Dec 2019 to modify the introduction, so excerpt is useful (if rust2020 summary post is ever updated. Original text below:

answering the Rust programming language call for blog posts as input to the 2020 roadmap

Caveat: I am new to Rust. There’s probably stuff I don’t even know about that is more important than anything here. This is just me doing my part to give back to the awesome Rust community.

]]>
/2019/12/rust-2020/feed/ 0
nut loaf with red pepper sauce /2019/11/nut-loaf-with-red-pepper-sauce/ /2019/11/nut-loaf-with-red-pepper-sauce/#respond Sat, 30 Nov 2019 02:27:36 +0000 /?p=6887 Continue reading ]]> Slightly modified vegan nut loaf to avoid tomatoes. Nut loaf recipe can be also be used to make burger patties, pan fried or baked, or can be stuffed into puff pastry for a puff pastry loaf or small hand pies.

Requires food processor to grind up various ingredients.

Ingredients

Nut loaf

1 1/4 cups (180 g) nuts (raw or roasted cashews, raw walnuts, raw pecans)
1/4 cup (33.5 g) sunflower seeds (or more cashews)
2 tsp oil (or 1/4 cup broth)
1 cup (160 g) chopped onion
4 cloves of garlic chopped
1 cup (96 g) chopped mushrooms (shitake or hen of the wood)
1 cup (140 g) cubed butternut squash
1 tsp each thyme, sage, rosemary, oregano
1 tsp smoked paprika
1/2 tsp black pepper
1/4 tsp each cinnamon and nutmeg
1/2 to 3/4 tsp salt
2 tbsp soy sauce or tamari
2 eggs (or 2 flax eggs for vegan)
2/3 cup (81 g) breadcrumbs

Sauce

1/4 cup roasted red peppers
1.5 tbsp soy sauce/tamari (or use 1/4 tsp salt + 1 tbsp broth for soyfree)
2 tbsp maple syrup
2 tsp apple cider vinegar
1/2 tsp (0.5 tsp) garlic powder

Instructions

Preheat the oven to 350 degrees F.

  1. Toast the raw nuts and sunflower seeds in the oven at 325 deg F (160 C) for 5 mins.
  2. Food processor: pulse nuts and seeds to a somewhat coarse meal
  3. In skillet over medium heat for 2 mins
    • oil or broth.
    • onion, garlic and a pinch of salt
  4. Add mushrooms and a pinch of salt and cook until some golden edges. (3-4 mins)
  5. Add butternut squash and mix in. Add a splash of water, cover and cook until the squash is tender.
  6. Mash and transfer to a bowl.
  7. Add in the spices, salt and mix in.
  8. Add the chopped nuts and seeds, soy sauce, eggs, breadcrumbs and mix well.
  9. Taste and adjust salt, herbs and flavor. ** The flavor will get more pronounced on baking. If the mixture is too wet, add a tbsp or so flour. You want it to be just slightly sticky. If too dry or crumbly, add a splash of broth. If you like sweeter, profile, add a tbsp of tomato paste or some chopped dried fruit such as dried cranberries or apricots.
  10. Transfer to a parchment lined pan. (I used lightly oiled cookie sheet.) Lightly press to shape. Do not pack too much.
  11. Bake for 25 to 30 mins.

Sauce:
1. Blend roasted red peppers into a smooth paste
2. Mix the rest of ingredients in a sauce pan over low heat for 15-20 minutes.
3. Taste and adjust as needed.

Take the loaf out of the oven. Spread the glaze over the loaf and then bake again for 20 to 30 mins. Let cool for 15 mins before removing from the pan. Then cool completely before slicing. Serve with gravy or cranberry sauce or both!

]]>
/2019/11/nut-loaf-with-red-pepper-sauce/feed/ 0