10 interesting stories served every morning and every evening.

A backdoor in a LinkedIn job offer

roman.pt

Last week, I got a LinkedIn mes­sage from a re­cruiter at a small crypto startup. We ex­changed a few mes­sages over a cou­ple of days, she de­scribed a bro­ken proof-of-con­cept they needed a lead en­gi­neer for, and then sent me a pub­lic GitHub repo to re­view. Specifically, she asked me to check out the dep­re­cated Node mod­ules is­sue.”

It’s not un­com­mon to ask for a re­view of an ex­ist­ing code­base, but some­thing felt off and raised an alarm in my head, so I de­cided to get a bit ex­tra para­noid.

Instead of cloning and in­stalling de­pen­den­cies, I spun up a throw­away VPS on Hetzner, cloned the repo there, and pointed Pi at it in read-only mode, with only file-read­ing tools en­abled:

pi –tools read,grep,find,ls

I asked the agent to re­view the code­base and flag any­thing sus­pi­cious. It stopped al­most im­me­di­ately at app/​test/​in­dex.js.

The back­door

The repo felt like a React fron­tend with a Node back­end. The trap was in app/​test/​in­dex.js, about 250 lines dis­guised as a test suite. Inside, a URL is as­sem­bled from frag­ments:

const pro­to­col = https”, do­main = store”, sep­a­ra­tor = ://”, path = /icons/”, to­ken = 77″, sub­do­main = rest-icon-handler”, bear­rto­ken = logo”;

These com­bine into https://​rest-icon-han­dler.store/​icons/​77.

Then, buried be­tween walls of com­mented-out tests, the pay­load runs any­thing the server sends back to your ma­chine.

The pay­load on line 225, hid­ing in plain sight be­tween com­mented-out tests.

How it trig­gers

The file does­n’t wait for the tests to run. app/​in­dex.js it­self ex­e­cutes const test = re­quire(‘./​test’), which loads and runs app/​test/​in­dex.js.

pack­age.json wires app/​in­dex.js into startup:

pre­pare runs app:pre, which is node app/​in­dex.js.

The pre­pare script is the im­por­tant one. npm runs pre­pare au­to­mat­i­cally af­ter npm in­stall, so just in­stalling de­pen­den­cies ex­e­cutes the back­door.

The in­struc­tion to check out the dep­re­cated Node mod­ules is­sue” was bait to get me to run npm in­stall.

I could have let the pay­load run in the sand­box and watched what the server sent back as the sec­ond stage, but I stopped there. A repo that runs what­ever a server hands it was enough ev­i­dence.

A bor­rowed iden­tity

The com­mits in the repo were au­thored un­der the name and email of a real de­vel­oper, a full-stack en­gi­neer with an or­di­nary LinkedIn pro­file, a per­sonal web­site, and a GitHub ac­count with a long his­tory. I mes­saged him, pre­tend­ing I’d in­her­ited the code­base and had a few im­ple­men­ta­tion ques­tions, to see how he’d re­act.

He told me he’d never worked for them. He’d been im­per­son­ated on GitHub be­fore and had a repo taken down over it, and he had noth­ing to do with this one. He was re­port­ing these re­pos too.

The whole com­mit his­tory, 39 com­mits, at­trib­uted to one de­vel­oper who’d never touched the repo.

A sec­ond bor­rowed iden­tity

The re­cruiter’s pro­file be­longed to a real arts jour­nal­ist, a well-known one I looked up later, with a long cul­tural back­ground and noth­ing tech­ni­cal on it. When I played along and told her I could­n’t get the pro­ject to in­stall, the jour­nal­ist in­stantly turned into an ex­pert on npm and Node ver­sions. It was quite amus­ing, I’d say.

The non-tech­ni­cal re­cruiter, sud­denly de­bat­ing Node ver­sions and push­ing me to run npm in­stall.

This can hap­pen to any­one

I’ve heard of these at­tacks and read about them on HN, but when one came af­ter me it still caught me a bit off guard. I sus­pected some­thing from the first few mes­sages, but on a more tired or rushed day, I could eas­ily have run npm in­stall be­fore think­ing it through. So, if you get a LinkedIn mes­sage ask­ing you to re­view a repo, a bit of para­noia and good se­cu­rity hy­giene never hurts.

Another take­away is that re­view­ing the code with a read-only agent turned out more pro­duc­tive than read­ing it my­self. The back­door was dressed up as sloppy be­gin­ner code, but the agent flagged it in sec­onds.

I re­ported the repo to GitHub and the re­cruiter to LinkedIn. So far noth­ing has changed and the code is still up.

Running local models is good now

vickiboykis.com

I’ve been work­ing with lo­cal mod­els since they came out, and fi­nally, they’re sur­pris­ingly good now.

I have a 2022 M2 Mac with 64 GB RAM and 1TB stor­age and I’ve used

Mistral 7B

Gemma 3

OpenAI OSS-20B

Qwen 3 MOE, as well as a num­ber of other Qwen vari­ants like Qwen 2.5 Coder

across a lot of dif­fer­ent sys­tem se­tups like

raw llama.cpp with Open WebUI

llama-cpp-python

Ollama

lla­mafiles and

LM Studio

Where are lo­cal mod­els now?

Early on, mod­els were slow, hard to use, and just not that ac­cu­rate for most pro­gram­ming tasks. The idea that lo­cal mod­els were se­verely lag­ging be­hind was largely true un­til, for me, the re­lease of GPT-OSS. I have no con­crete sci­en­tific ev­i­dence of this - my own per­sonal vibe met­ric of is a model good enough” is, do I have to dou­ble-check it against an API model”, and GPT-OSS was the first one where I started do­ing that a lot less of­ten.

As a re­sult, I’ve mostly been us­ing lo­cal mod­els as fast, per­son­al­ized Google for de­vel­op­ment ques­tions that don’t re­quire re­cency.

But with the most re­cent re­leases from Google in the Gemma 4, fam­ily, I’ve fi­nally been able to do agen­tic cod­ing lo­cally and have loops work at about ~75% the ac­cu­racy/​speed of fron­tier mod­els, which is in­cred­i­ble.

I’ve so far been us­ing gemma-4 – 26b-a4b LM Studio im­ple­men­ta­tion as my de­fault lo­cal model. I’ve used the lo­cal setup so far to: Refactor a Python script that was a note­book into a repo of 5 – 6 mod­ules, lint that mod­ule to use cor­rect type hints for gener­ics (most fron­tier mod­els now do this au­to­mat­i­cally, but not al­ways).

I’ve also used it to proof­read some blog posts, write unit tests, and to boot­strap a repo that stands up a two-tower model for rec­om­men­da­tions just to see what the agent would do with a blank slate. Here’s what it gen­er­ated, which was pretty ba­sic but still be­yond the scope of any­thing I would have thought pos­si­ble last year:

Note that the en­vi­ron­ment is re­stricted be­cause I run all my agen­tic work­flows in a Docker con­tainer with lim­ited ac­cess to ex­e­cu­tion.

I’m also build­ing an app that sur­faces trend­ing top­ics from Arxiv pa­pers. Out of cu­rios­ity, I had Pi go through my past LM Studio ses­sion logs and fig­ure out what I was us­ing LM Studio for:

Unsurprisingly, since I’ve been work­ing on Rijksearch,

None of these are ground­break­ing tasks (again, a lot of per­son­al­ized Google/docs lookups), and work­ing on them does give my GPUs and RAM a work­out and the K-V cache grows to 64 GB RAM.

But, the larger story for me is that these kinds of tasks, even as sim­ple as they are, used to be im­pos­si­ble for lo­cal mod­els as re­cently as 6 months ago.

Gemma-4 – 12b-qat just came out but I’ve al­ready also re­ally been im­pressed with its per­for­mance rel­a­tive to its size. The model ar­chi­tec­ture it­self is re­ally in­ter­est­ing and pro­poses a bunch of in­ter­est­ing ques­tions like, if we are con­strained by per­for­mance and price, what ar­chi­tec­tural trade­offs do we need to make?” a ques­tion that so far has not re­ally been asked in the mad to­ken gold rush.

Running agen­tic mod­els lo­cally to­day

But don’t take my word for any of this, try it out for your­self! You’ll need a lo­cal model in­fer­ence en­gine, an agen­tic har­ness, and the lo­cal model ar­ti­fact if you want to try to run lo­cal agen­tic flows. You’ll need to set up the har­ness to point at your lo­cal in­fer­ence end­point, the down­loaded model ar­ti­fact served via the in­fer­ence en­gine.

For my lo­cal setup, I’m cur­rently us­ing Pi as the agent har­ness and LM Studio as the in­fer­ence server, al­though it would likely be faster if I just used llama.cpp di­rectly - a po­ten­tial di­rec­tion for a fu­ture ex­per­i­ment.

This post was very easy to fol­low to set up agen­tic cod­ing with Pi and LM Studio, al­though I did make a few tweaks to the post’s setup.

Model: The post rec­om­mends Gemma 26B A4B , but gemma-4 – 12b-qat is more re­cent and smaller and faster, with­out much sac­ri­fice in ac­cu­racy.

Security: I run every Pi ses­sion in a Docker con­tainer and give it per­mis­sions only to bash so that it can’t run Python code or do web brows­ing, al­though I do plan to al­low curl in a dif­fer­ent im­age for some re­search work I’m do­ing.

Agent Harness Config: Since I run every­thing in Docker, I edited Pi’s mod­els.json in or­der to get Pi to talk to the model.

lmstudio”: { baseUrl”: http://​host.docker.in­ter­nal:1234/​v1, api”: openai-completions”, apiKey”: not-needed”, models”: [ { id”: google/gemma-4 – 12b-qat”, input”: [ text”, image” ] } ] }

Here’s my Docker Compose con­fig:

ser­vices: pi: build: con­text: . dock­er­file: Dockerfile im­age: pi-agent:0.74.0 init: true stdin_open: true tty: true ex­tra_hosts: - host.docker.internal:host-gateway” en­vi­ron­ment: ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-} OPENAI_API_KEY: ${OPENAI_API_KEY:-not-needed} GEMINI_API_KEY: ${GEMINI_API_KEY:-} OPENAI_API_BASE: ${OPENAI_API_BASE:-http://​host.docker.in­ter­nal:1234/​v1} # note that you’ll need to spec­ify a base if you also use OpenAI to ac­cess OpenAI’s ac­tual com­ple­tions end­point WHATEVER_API_KEY: ${WHATEVER_API_KEY:-} vol­umes: - ${HOME}/.pi/agent/models.json:/config/models.json - ${WORKSPACE:-.}:/workspace - pi-con­fig:/​con­fig - pi-ses­sions:/​ses­sions work­ing_dir: /workspace

vol­umes: pi-con­fig: pi-ses­sions:

and here’s the bash script that runs pi .

#!/usr/bin/env bash

# Pi — Start the con­tainer­ized Pi agent.

# Directory con­tain­ing this script and the com­pose files. SCRIPT_DIR=“$(cd — “$(dirname ${BASH_SOURCE[0]}“)” && pwd)”

# Workspace to mount into the con­tainer. WORKSPACE_DIR=“${WORKSPACE:-$(pwd)}” case $WORKSPACE_DIR” in /*) ;; *) WORKSPACE_DIR=“$(cd — “$WORKSPACE_DIR” && pwd)” ;; esac ex­port WORKSPACE=“$WORKSPACE_DIR”

sand­box=“${PI_SAND­BOX:-0}” pi_args=()

while (($#)); do case $1” in

–sandbox) sand­box=1 ;; –no-sandbox) sand­box=0 ;; *) pi_args+=(“$1”) ;;

esac shift done

com­pose_­files=( -f $SCRIPT_DIR/docker-compose.yml” ) if [[ $sandbox” == 1” ]]; then # an even more se­cure sand­box com­pose_­files+=( -f $SCRIPT_DIR/docker-compose.sandbox.yml” ) fi

# Derive a con­tainer name from the work­space di­rec­to­ry’s base­name. # Sanitize to char­ac­ters Docker ac­cepts: [a-zA-Z0 – 9][a-zA-Z0 – 9_.-]* re­po_s­lug=“$(base­name — “$WORKSPACE_DIR” | tr -c a-zA-Z0 – 9_.-’ -’ | sed s/^-*//’)” [[ -z $repo_slug” ]] && re­po_s­lug=“work­space” con­tain­er_­name=“pi-${re­po_s­lug}-$$”

api_key_args=( -e OPENAI_API_KEY -e DEEPSEEK_API_KEY -e ANTHROPIC_API_KEY -e GEMINI_API_KEY )

cmd=( docker com­pose –project-directory $SCRIPT_DIR” ${compose_files[@]}” run –rm –name $container_name” ${api_key_args[@]}” pi )

if ((${#pi_args[@]})); then cmd+=(“${pi_args[@]}“) fi

exec ${cmd[@]}”

I build the Docker con­tainer and make changes to the files in its own repo. Then, I run Pi in the repo I’m work­ing in, which spins up Docker so that Pi can’t wipe files or di­rec­to­ries by act­ing on my phys­i­cal hard drive. This also en­ables Pi run­ning in the con­tainer to see my cus­tom model json con­fig by ship­ping it into the con­tainer. All of this has been work­ing fairly well for my ex­per­i­ments.

There are still is­sues with lo­cal mod­els: in­fer­ence can be slow, con­text win­dows are small and lim­ited to your own hard­ware, and the ecosys­tem, al­though it’s made a ton eas­ier by tool­ing like LM Studio and HuggingFace’s Use This Model but­ton. Early re­leases suf­fer from prompt tem­plate mis­matches. But, these are usu­ally patched ex­tremely quickly. Needless to say, I’m not sure this is ready for pro­duc­tion soft­ware de­vel­op­ment quite yet.

The ben­e­fits, though, are nu­mer­ous and the ecosys­tem crit­i­cal to in­vest in, par­tic­u­larly now. One of the very cool parts of lo­cal mod­els is you can in­tro­spect al­most every­thing, like watch­ing the to­ken in­fer­ence process live,

and watch­ing to­kens in/​out.

You can do things like change the lo­cal con­text win­dow and watch per­for­mance im­prove or de­grade, and re­ally dig into how your to­kens are processed on the GPU. You can change the sys­tem prompt, the quan­ti­za­tions. You can pit mod­els against each other. You can also change and in­tro­spect the har­ness side.

The pos­si­bil­i­ties are end­less, and the tools only keep get­ting bet­ter.

reuters.com

www.reuters.com

Please en­able JS and dis­able any ad blocker

Banned Book Library

www.richardosgood.com

Overview

A long while back I had an idea to hack a WiFi smart light bulb to do some­thing more use­ful to me. Actually, I had a few dif­fer­ent ideas of things to do with them. One of these ideas was to mod­ify the de­vice to have an open WiFi ac­cess point and a web server host­ing banned books. The idea was that if you lived some­where that banned books you thought were im­por­tant, you could the­o­ret­i­cally stick a dig­i­tal copy of the book on one of these light bulbs. Then you could go in­stall it some­where in your com­mu­nity. As long as the light bulb is switched on, then any­one in the vicin­ity can still ac­cess the banned ma­te­r­ial as­sum­ing they have an elec­tronic de­vice with WiFi. Since the de­vice is a light bulb, it would be dif­fi­cult to de­tect and likely to go un­no­ticed. A cy­ber­punk dig­i­tal dead drop. These de­vices are also fairly in­ex­pen­sive, so leav­ing them around town as is hope­fully not very cost pro­hib­i­tive.

I think the idea host­ing banned books specif­i­cally came to me af­ter hav­ing read Ben Brown’s short story Library. It’s been a while since I read it, but if I re­call there are char­ac­ters in the story who main­tain a library” which acts as a dig­i­tal archive of cre­ative works, own­ers man­u­als, 3d mod­els, etc. Things that oth­ers might find use­ful or in­ter­est­ing that you would­n’t want to lose should they be some­how wiped from the Internet. That’s only a part of the story and it was a fun read. You should go read it!

Anyway, a few months ago I de­cided to fi­nally get to work on this pro­ject. The re­sult is the Banned Book Library!

Hardware

I brought up this idea with some folks at my lo­cal DEFCON meetup group. One of them had some ex­pe­ri­ence with home au­toma­tion and rec­om­mended I look into Tasmota. Tasmota is an open-source firmware you can in­stall on var­i­ous smart de­vices to in­te­grate them into a home au­toma­tion sys­tem such as HomeAssistant. The main idea with this firmware is to pro­vide you with lo­cal con­trol over the de­vice. Many of these de­vices rely on cloud ser­vices that change over time or some­times com­pletely dis­ap­pear, leav­ing the de­vices un­us­able. Tasmota al­lows you to un­tether your­self from these cloud ser­vices and host every­thing in­ter­nally. Actually, this is an­other great par­al­lel to Ben Brown’s Library story. Also rel­e­vant is Cory Doctorow’s Unauthorized Bread.

I had­n’t heard of Tasmota but af­ter read­ing about it, it sounded like a good way to go. I had sort of ex­pected many of these smart light bulbs would rely on ESP32 chips, or sim­i­lar. Having no ex­pe­ri­ence with them made it feel a bit daunt­ing to get started. I thought maybe it might be eas­ier to mod­ify the Tasmota firmware to do what I wanted in­stead of writ­ing some­thing from scratch. I did not end up mod­i­fy­ing Tasmota in the end, but this rab­bit hole did lead me to find a web­site that sells WiFi light bulbs with Tasmota pre-in­stalled. The prod­uct page even spec­i­fied that the bulb uses an ESP32C3 4MB. It also listed which GPIO pins were used to con­trol the var­i­ous LEDs, which would come in handy later:

R:GPIO6 G:GPIO7 B:GPIO5 CW:GPIO3 WW:GPIO4

This seemed like a great start­ing point be­cause al­though Tasmota sup­ports many other de­vices, not all of them can be flashed over the air (OTA). Many of them re­quire break­ing them open, sol­der­ing on small wires, and flash­ing via a se­r­ial pro­gram­mer. Tasmota has a built-in mech­a­nism to up­date the firmware OTA, so it seemed likely I might be able to flash my own mod­i­fied Tasmota firmware, or oth­er­wise a cus­tom firmware with­out hav­ing to tear the light bulbs apart.

The one thing that struck me as a po­ten­tial prob­lem was the flash size. It was listed as 4MB. This is not very much space to host a li­brary of books… That 4MB would need to fit all of the firmware, the web­site, and any books. Not much space. I thought I might be able to over­come this by adding stor­age, such as a mi­croSD card reader. More on that later.

I pur­chased two of these bulbs to play with. I fig­ured I might end up break­ing or brick­ing one, so hav­ing a backup would be good.

Teardown

The bulbs showed up in the main a few days later and I opened up the box to check it out.

The first thing I wanted to do was open it up and see what I was work­ing with. I was mainly won­der­ing if the pins were ex­posed so I might be able to at­tach a mi­croSD card reader. To re­move the white plas­tic bulb on top, I ran a razer blade around the cir­cum­fer­ence of the bulb in be­tween the base and the bulb. I had to go around twice, the sec­ond time an­gling the knife down­ward to cut through the sealant in­side. Then I was able to just twist and pull the bulb right off. Minimal dam­age.

This re­vealed a round daugh­ter board with all of the LEDs on it. This PCB was at­tached to an­other one un­der­neath us­ing six pins. There was also a hole in the mid­dle where the mother board stuck through a bit. This ended up be­ing the an­tenna for the ESP32. The bulb hous­ing was lined with alu­minum and the daugh­ter board was also made of alu­minum. So they likely de­signed it this way to en­sure a de­cent wifi sig­nal.

The daugh­ter board was glued in with more sealant. I used my knife to cut through this and a small, flat screw­driver to care­fully pry the daugh­ter board out. I slid it up so it would sep­a­rate from the mother board.

Now I could very clearly see the ESP32C3 in­side, as well as some other sup­port­ing cir­cuitry. I’m no elec­tron­ics ex­pert, but I be­lieve most of the com­po­nents in­side are to con­vert the AC mains power to a cleaner 3.3V DC for the ESP32 as well as what­ever volt­age was needed to drive the LEDs. I never plugged this de­vice into mains while it was open so I did­n’t mea­sure the volt­age for the LEDs.

One nice thing about this ESP32 was that it seemed to have a bunch of pins ex­posed. I hoped this might make it pos­si­ble to sol­der on a mi­croSD card reader for ex­panded stor­age. You can also see some of the pins are la­beled at the bot­tom to let you know which pins are for which col­ors.

There was re­ally no way to get a sol­der­ing iron in­side the bulb. The ESP32 only had the an­tenna por­tion stick­ing out above the hous­ing. The only way I was go­ing to sol­der any wires to those pins was to re­move the mother board. Unfortunately this was not a sim­ple task. The mother board was held in place with some kind of rub­bery pot­ting com­pound. There was… a lot of it. I had to dig it out with a knife and screw­driver and then yank the board out.

I chipped away a bunch of the com­pound from the mother board to get a bet­ter look at it. It made a mess.

This was a huge pain and not some­thing I would want to be a re­quired step in the process of set­ting up one of these dead drops. I re­ally wanted this pro­ject to be as ac­ces­si­ble as pos­si­ble, re­quir­ing min­i­mal tools and hard­ware skills. Not only that, but there re­ally was no way I was go­ing to get this re-in­stalled prop­erly. And even if I man­aged to get it back in, I would­n’t trust it to be safe. It could be­come a fire haz­ard for all I know.

All that said, this did give me a bit of a de­vel­op­ment plat­form to work from. I thought since I had this thing apart any­way, I might as well sol­der on some wires for se­r­ial pro­gram­ming. I had not done this be­fore, so I had to do some read­ing to fig­ure it out. Basically, I needed to power the chip with 3.3v and ground. Plus I need one wire each for the se­r­ial UART TX and RX pins. The first ques­tion to an­swer was which pins were the right pins?

I man­aged to find this ex­act mod­ule on AliExpress. The list­ing in­cluded an im­age of the back­side of the mod­ule, which thank­fully had la­beled all of the pins.

This helped me fig­ure out the VCC, GND, TX, and RX pins. For GND I ended up just sol­der­ing to the metal shield­ing as it was also grounded and much eas­ier to sol­der to. I sol­dered wires to all of the other pins. I had to re­move a few ca­pac­i­tors in or­der to get in there.

In or­der to pro­gram the de­vice via se­r­ial, you have to boot it into a spe­cial down­load mode. This seems to nor­mally in­volve short­ing one of the ESP32 pins to ground while it is pow­ered on. I can’t re­mem­ber how I fig­ured this out, but it ended up bing the IO9 pin in this case. So I sol­dered an­other wire there.

I set my bench top power sup­ply for 3.3v and hooked it up to the chip. Applying power to the VCC and GND wires did boot it up and I could see the IoTorerro ac­cess point wait­ing for me to con­nect and con­fig­ure the de­vice.

To get it into down­load mode, I pow­ered the de­vice off. I con­nected my FTDI de­vice to my lap­top’s USB port and then to the GND, TX, and RX wires on the mother board. Then I man­u­ally shorted the IO9 wire to the shield­ing to ground it. Then I pow­ered the de­vice on. I could see this time it was only draw­ing about 0.09 amps, which was much less than be­fore. So some­thing was dif­fer­ent.

I thought the first thing I should prob­a­bly do is try to dump the en­tire firmware. This would hope­fully al­low me to flash it back to the de­vice to start it back over from a clean state. I used es­p­tool to do this.

es­p­tool –chip es­p32c3 –port /dev/ttyUSB0 –baud 114200 read-flash 0x0 0x4000000 ./tasmota_original_firmware.bin

I could see that it was able to talk to the de­vice and af­ter a few min­utes I had a firmware dump! Things were look­ing good so far.

Early Experiments

Hello World

Early on I went look­ing at the Tasmota source code to see if I could mod­ify it to act as the Banned Book Library. The firmware was much more com­pli­cated than I had an­tic­i­pated. Not only that but it sup­ported all dif­fer­ent ar­chi­tec­tures and de­vices. It also had many fea­tures I re­ally did­n’t need. And con­sid­er­ing the pur­pose of my pro­ject, I wanted to try and keep the firmware bloat down to make more space for book stor­age. So I scrapped the idea of mod­i­fy­ing Tasmota.

I then dis­cov­ered that you can pro­gram ESP32 de­vices with Arduino. I had used Arduino a bunch maybe 10 – 15 years ago, so I had some ex­pe­ri­ence with it. I re­called it be­ing pretty ac­ces­si­ble and mak­ing it eas­ier to work with em­bed­ded sys­tems. But I was def­i­nitely rusty and I had never used it to pro­gram an ESP32 be­fore.

I setup the Arduino IDE on my lap­top and con­fig­ured it to use my ttyUSB0 se­r­ial pro­gram­mer as well as the proper ESP32C3 chip. I then wrote a very ba­sic hello world pro­gram to just send a mes­sage over the se­r­ial port back to the lap­top. This would let me test and see if I could flash the firmware and get the de­vice to do some­thing new.

I used the Arduino IDEs built in up­load fea­ture to up­load the code to the de­vice. It took care of the com­pli­cated stuff and just did it. I checked the se­r­ial mon­i­tor and found that it was work­ing! I did get se­r­ial out­put from the de­vice. So I was able to write my own firmware to this thing.

Web Server

The next thing I wanted to do was setup an open WiFi ac­cess point and Web Server. I be­lieve I started with this tu­to­r­ial to get an idea of what to do, though I mod­i­fied it since I was­n’t in­ter­ested in con­trol­ling an LED at the time. I later switched to us­ing Async Web Server and used this tu­to­r­ial to get a han­dle on things.

MicroSD Card

After get­ting that work­ing I wanted to try and get a mi­croSD card work­ing. I pur­chased some break­out boards from Sparkfun.

I went read­ing the ESP32C3 datasheet to fig­ure out how to wire up the SD card reader. I man­aged to fig­ure it out even­tu­ally. However, in­stead of sol­der­ing to this de­vice, I de­cided to switch to us­ing an Adafruit ItsyBitsy ESP32 that I had lay­ing around un­used from a pre­vi­ous pro­ject. The ItsyBitsy was eas­ier to work with be­cause it breaks out all of the pins in such a way that I could sol­der on header pins. This made it re­ally easy to at­tach the mi­croSD card reader for pro­to­typ­ing.

Then I fol­lowed this other tu­to­r­ial to fig­ure out how to pro­gram the ESP32 to use the de­vice. I did end up get­ting this to work and even us­ing it to host files for the web server us­ing LittleFS, how­ever the en­tire idea of adding a mi­croSD did not work out so I won’t go into de­tail on this.

The real prob­lem with the mi­croSD card idea was that sol­der­ing wires onto this ESP32C3 in the ac­tual de­vice was a real pain. There was no way to do it with­out re­mov­ing the board from the hous­ing which ef­fec­tively de­stroys the de­vice as far as I’m con­cerned. I tried to get cre­ative.

First I looked at re­pur­pos­ing some of the LED con­troller pins. There were six pins go­ing from the mother board to the daugh­ter board. Five of those were for the var­i­ous LED col­ors: warm white, cool white, red, green, and blue. I did­n’t care about the RGB at all. And I could do away with ei­ther the warm or cool white if needed. However this did­n’t pan out. The way this de­vice is de­signed, the mother board sends power to the daugh­ter board via one pin. The other five pins route back to tran­sis­tors on the mother board. The ESP32 turns its GPIO pins high which then trig­gers the tran­sis­tors and com­pletes the cir­cuit for each color back to ground. This meant that the GPIO pins could only be used for out­put at best in this con­fig­u­ra­tion. No in­put.

I then had a crazy idea to make a clamp” that could clamp onto the top of the ESP32 and pos­si­bly al­low some header pins to make con­tact with the ex­posd ESP32 pins. I de­signed a small 3D-printable part that was in­tended to slip over the ESP32C3 and clamp into place.

This ended up be­ing way too finicky and un­re­li­able. After sev­eral it­er­a­tions of the de­sign, I aban­doned the idea al­to­gether.

Detour

At this point, I de­cided to try look­ing at some other bulbs. Maybe there were other de­vices out there that would lend them­selves bet­ter to sol­der­ing on some ex­tra com­po­nents? The prob­lem was I did­n’t want to break the bank buy­ing 20 dif­fer­ent LED bulbs just to see if the idea was even fea­si­ble. I started by look­ing into prior re­search. I found sev­eral tear­down ar­ti­cles fea­tur­ing var­i­ous smart light bulbs, but they all looked very sim­i­lar to my setup. It did re­veal that they don’t all use ESP32. At this point I had de­cided I only wanted to stick with the ESP32 since I had al­ready spent time learn­ing how to pro­gram it.

I bought a few bulbs from the lo­cal hard­ware store. One of them had a sim­i­lar de­sign, but ac­tu­ally had a bit of alu­minum pro­tect­ing the mother board that I could­n’t re­move safely.

The Philips WiZ looked promis­ing at first. It used an ESP32C3-mini-1 and the en­tire chip was ex­posed af­ter re­mov­ing only the plas­tic bulb!

Unfortunately none of the ESP32 pins were ac­ces­si­ble on this mod­ule. So there was no way to sol­der wires for any­thing I needed.

I also tore apart a few stan­dard LED bulbs with no smart” com­po­nents. I thought maybe I could just stick my own cir­cuit in­side. But this ended up look­ing more com­pli­cated and spe­cial­ized than just flash­ing the Tasmota bulb.

There was also an in­ter­est­ing DIY LED smart bulb pro­ject I found on Hackaday that in­trigued me, but I re­ally pre­ferred the idea of re­pur­pos­ing an off-the-shelf unit.

Ultimately I de­cided to stick with the Tasmota bulb and to just try to work within my 4MB lim­i­ta­tions.

The Storage Problem

To get a han­dle on the stor­age sit­u­a­tion, we can look at the ESP32 par­ti­tion table. The table is nor­mally stored at off­set 0x8000 in flash, so we can dump this sec­tion and then con­vert the bi­nary to a read­able CSV file.

$ es­p­tool -p /dev/ttyUSB0 –baud 115200 read­_flash 0x8000 0x1000 part_­dump.bin Warning: Deprecated: Command read_flash’ is dep­re­cated. Use read-flash’ in­stead. es­p­tool v5.1.0 Connected to ESP32-C3 on /dev/ttyUSB0: Chip type: ESP32-C3 (QFN32) (revision v0.4) Features: Wi-Fi, BT 5 (LE), Single Core, 160MHz, Embedded Flash 4MB (XMC) Crystal fre­quency: 40MHz MAC: 0c:4e:a0:31:cb:e4

Stub flasher is al­ready run­ning. No up­load is nec­es­sary.

Configuring flash size… Read 4096 bytes from 0x00008000 in 0.4 sec­onds (87.2 kbit/​s) to part_dump.bin’.

Hard re­set­ting via RTS pin…

I used gen_E­s­p32­part.py to gen­er­ate a csv file de­scrib­ing the par­ti­tions.

$ gen_e­s­p32­part.py part_­dump.bin Parsing bi­nary par­ti­tion in­put… Verifying table…

# ESP-IDF Partition Table # Name, Type, SubType, Offset, Size, Flags nvs,data,nvs,0x9000,20K, ota­data,data,ota,0x­e000,8K, safeboot,app,fac­tory,0x10000,832K, app0,app,ota_0,0x­e0000,2880K, spiffs,data,spiffs,0x3b0000,320K,

This re­vealed five par­ti­tions:

nvs

ota­data

safeboot

app0

spiffs

As I went through this pro­ject, I even­tu­ally learned that nvs is used for non-volatile stor­age. This is where the main firmware can store con­fig­u­ra­tion set­tings like the WiFi net­work, pass­word, LED color, etc. That way when it re­boots, it can re­mem­ber these set­tings.

I’m not sure what ota­data is used for ex­actly, other than it has some­thing to do with over the air up­dates.

The safeboot par­ti­tion is a sec­ond bootable firmware that Tasmota uses to flash the main firmware. It seems that the usual way of deal­ing with OTA up­dates is to have two du­pli­cate firmware par­ti­tions of the same size. You boot from par­ti­tion A, and then when you in­stall an up­date it gets writ­ten to par­ti­tion B. Then you re­boot into par­ti­tion B. If every­thing looks fine, the firmware can then be flashed to par­ti­tion A. This way if a firmware up­date fails on par­ti­tion B, the de­vice can re­cover by re­boot­ing into par­ti­tion A. The down­side with this method is that you need to firmware par­ti­tions of equal size. This takes up a lot of space.

Tasmota does things a bit dif­fer­ently in the safeboot con­fig­u­ra­tion. Instead of hav­ing two du­pli­cate firmware im­ages, there is the main firmware stored in the app0 par­ti­tion. It then has a sec­ond, smaller firmware stored in safeboot. The safeboot firmware can con­nect to a pre-con­fig­ured WiFi net­work and flash the app0 par­ti­tion and that’s about it as far as I can tell. You can’t even use the safeboot firmware to con­fig­ure WiFi. That must be done via the main firmware. Safeboot must read the set­tings from nvs. The ben­e­fit of do­ing things this way is that the main Tasmota firmware can be larger with more fea­tures with­out tak­ing up dou­ble the space for OTA up­dates. More info on this can be found here.

Finally there is the spiffs par­ti­tion. spiffs is a file sys­tem type but in this case can also rep­re­sent a more mod­ern LittleFS file sys­tem. It’s ba­si­cally a small par­ti­tion to store files.

With this con­fig­u­ra­tion, the main firmware had close to 3MB of space and the safeboot was close to 1MB. There was just 320K for stor­age. That might fit one ebook, de­pend­ing on the length. Not ideal.

It oc­curred to me that I likely did­n’t need 2880KB to store my own firmware since mine would be much sim­pler than Tasmota. I thought I might be able to ad­just the par­ti­tion size from in the firmware it­self to shrink the app0 par­ti­tion and grow the spiffs par­ti­tion. That would give more space for web files and books.

I even­tu­ally did fig­ure out there was a way to do this thanks to this blog post.

Editing the par­ti­tion table is risky be­cause if it gets cor­rupted the de­vice may not boot and would only be re­cov­er­able via se­r­ial pro­gram­ming. This is not ideal, but the whole pro­ject is a hack so I guess what the hell?

The par­ti­tion table is stored at off­set 0x8000 in flash mem­ory. So re­ally all we need to do is over­write the table with what­ever we want it to be. We can’t just change the par­ti­tion off­sets and sizes though, be­cause there is an MD5 check­sum value at the end of the table data. Therefore we would need to up­date this value as well or the de­vice will not boot. We also can’t move the app0 par­ti­tion while we are booted into that par­ti­tion or else we will not be able to boot back to this fir­w­mare as it will not be lined up to the start of the par­ti­tion.

I mod­i­fied the par­ti­tion.csv file to look how I wanted the par­ti­tions to look and saved it as par­ti­tions.csv.new:

# Name, Type, SubType, Offset, Size, Flags nvs, data, nvs, 0x9000, 0x5000, ota­data, data, ota, 0xe000, 0x2000, safeboot, app, ota_1, 0x10000, 0xD0000, app0, app, ota_0, 0xE0000, 0x120000, spiffs, data, spiffs, 0x200000,0x200000,

This would al­low for 2MB of data for web server files and books in the SPIFFS par­ti­tion, which felt like enough to be at least use­ful. Then I used gen_e­s­p32­part.py to gen­er­ate an ac­tual par­ti­tion table bi­nary blob from the csv file:

$ gen_e­s­p32­part.py par­ti­tions.csv.new par­ti­tion­s_new.bin

I used xxd to out­put the im­por­tant bits in c ar­ray for­mat:

rick@nixlap ~/Projects/BannedBookLibrary/idf/library/main$ xxd -i ../partitions_new.bin |head -n17 ✭main un­signed char ___partitions_new_bin[] = { 0xaa, 0x50, 0x01, 0x02, 0x00, 0x90, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x6e, 0x76, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x50, 0x01, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x6f, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x50, 0x00, 0x11, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x73, 0x61, 0x66, 0x65, 0x62, 0x6f, 0x6f, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x50, 0x00, 0x10, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x12, 0x00, 0x61, 0x70, 0x70, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x50, 0x01, 0x82, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x73, 0x70, 0x69, 0x66, 0x66, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xeb, 0xeb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xda, 0xa8, 0x74, 0x2c, 0xcd, 0xc5, 0x28, 0xab, 0xd5, 0x0d, 0xf6, 0x41, 0xd3, 0xa7, 0xdd,

I then dropped it in a par­ti­tion.h file:

un­signed char par­ti­tion_table[] = { 0xaa, 0x50, 0x01, 0x02, 0x00, 0x90, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x6e, 0x76, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x50, 0x01, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x6f, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x50, 0x00, 0x11, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x73, 0x61, 0x66, 0x65, 0x62, 0x6f, 0x6f, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x50, 0x00, 0x10, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x12, 0x00, 0x61, 0x70, 0x70, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x50, 0x01, 0x82, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x73, 0x70, 0x69, 0x66, 0x66, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xeb, 0xeb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xda, 0xa8, 0x74, 0x2c, 0xcd, 0xc5, 0x28, 0xab, 0xd5, 0x0d, 0xf6, 0x41, 0xd3, 0xa7, 0xdd }; un­signed int par­ti­tion_table_len = 192;

I then wrote a func­tion to over­write the par­ti­tion table data with this in­for­ma­tion. The func­tion first checks to see if the par­ti­tion table MD5 sum al­ready matches the new table. If so, it’s al­ready been flashed and does­n’t need to be flashed again. If not, then it up­dates the par­ti­tion table.

bool ed­it_­par­ti­tion_table() {

int re­sult = es­p_flash_init(es­p_flash_de­fault­_chip); Serial.printf(“esp_flash_init re­sult: 0x%x\n”, re­sult);

uin­t8_t cur­ren­t_md5[MD5­SUM_­SIZE]; mem­set(cur­ren­t_md5, 0x0, MD5SUM_SIZE); re­sult = es­p_flash_read(es­p_flash_de­fault­_chip, cur­ren­t_md5, CONFIG_PARTITION_TABLE_OFFSET + OFFSET_TO_PART_MD5SUM, MD5SUM_SIZE); Serial.printf(“esp_flash_read re­sult: 0x%x\n”, re­sult);

if (memcmp(partition_new_md5, cur­ren­t_md5, MD5SUM_SIZE) != 0) { Serial.printf(“Patching par­ti­tion table…\n”); re­sult = es­p_flash_erase_re­gion(es­p_flash_de­fault­_chip, CONFIG_PARTITION_TABLE_OFFSET, 0x1000); Serial.printf(“esp_flash_erase_region re­sult: 0x%x\n”, re­sult);

re­sult = es­p_flash_write(es­p_flash_de­fault­_chip, par­ti­tion_table, CONFIG_PARTITION_TABLE_OFFSET, par­ti­tion_table_len); Serial.printf(“esp_flash_write re­sult: 0x%x\n”, re­sult);

Serial.printf(“Erasing NVS par­ti­tion…\n”); re­sult = es­p_flash_erase_re­gion(es­p_flash_de­fault­_chip, 0x9000, 0x5000); Serial.printf(“esp_flash_erase_region re­sult: 0x%x\n”, re­sult);

Serial.printf(“Setting de­fault boot par­ti­tion\n”); const es­p_­par­ti­tion_t * part = es­p_­par­ti­tion_find­_­first(ES­P_­PAR­TI­TION_­TYPE­_APP, ESP_PARTITION_SUBTYPE_APP_OTA_0, app0″); es­p_o­ta_set_­boot_­par­ti­tion(part);

Serial.printf(“Restarting…\n”); ESP.restart(); } else { Serial.printf(“Partition table al­ready patched\n”); }

re­turn true; }

This did not work at first. Whenever I tried read­ing from or writ­ing to the par­ti­tion table, the API func­tions would re­turn a suc­cess code but would­n’t ac­tu­ally read or write any­thing. It took some re­search and test­ing but even­tu­ally I dis­cov­ered that the ESP32 frame­work does­n’t al­low you to ac­cess cer­tain sen­si­tive ar­eas of the flash mem­ory for safety rea­sons. This in­cludes the boot­loader and the par­ti­tion table. When us­ing Arduino IDE to pro­gram an ESP32 the frame­work is pre­con­fig­ured for you, which makes things eas­ier in many cases. However, one of the con­fig­u­ra­tions has this safety fea­ture en­abled. This meant that I was­n’t go­ing to be able to edit the par­ti­tion table us­ing Arduino.

ESP-IDF

After some more re­search I dis­cov­ered that the of­fi­cial ESP32 frame­work is called ESP-IDF. It’s more com­pli­cated to setup and use but of­fers greater con­trol over the de­vice and the frame­work it­self.

Feds freaked over Fable 5 after simple 'fix this code' prompt, not jailbreak, says researcher

www.theregister.com

REG AD

se­cu­rity

According to the one per­son who ac­tu­ally read the re­search pa­per

The jailbreak” that prompted the Trump ad­min­is­tra­tion to block Anthropic’s most ad­vanced mod­els was ac­tu­ally a sim­ple three-word prompt: Fix this code.”

That’s ac­cord­ing to Katie Moussouris, founder and CEO of Luta Security, and the fairy god­mother of bug boun­ties. She says she was the only out­side ex­pert to read the third-party re­search pa­per on the Fable 5 guardrail by­pass tech­niques that prompted the ban.

On Friday, the US gov­ern­ment, re­port­edly cit­ing na­tional se­cu­rity con­cerns, is­sued an ex­port con­trol di­rec­tive to sus­pend ac­cess to Fable 5 and Mythos 5 by any for­eign na­tional, in­side or out­side the United States. In re­sponse, Anthropic dis­abled both mod­els for all our cus­tomers to en­sure com­pli­ance.”

REG AD

Anthropic shared the re­port pri­vately with her, Moussouris wrote in a Monday blog post.

REG AD

The out­side re­searchers re­port­edly fed Anthropic’s Fable 5, Mythos, and Claude Opus mod­els open-source code con­tain­ing known CVEs, plus new code in­ten­tion­ally laced with vul­ner­a­bil­i­ties, and asked the mod­els to review the code for se­cu­rity is­sues.”

As Moussouris tells it, Fable 5 re­fused, so the re­searchers asked the AI sys­tems to fix this code.” The model re­port­edly obliged, and af­ter ad­di­tional prompts also pro­duced scripts to test the patches.

That’s it,” Moussouris wrote. ‘Fix this code,’ plus sev­eral man­ual steps to gen­er­ate test scripts, should never have trig­gered an ex­port con­trol. I feel like mak­ing 90s-style t-shirts with fix this code’ on the front and this shirt is a mu­ni­tion’ on the back.”

Between 2013 and 2017, Moussouris served on the tech­ni­cal ex­pert group that rene­go­ti­ated the Wassenaar Arrangement, a vol­un­tary agree­ment be­tween 42 na­tions that gov­erns cer­tain ex­port con­trols for clas­si­fied dual-use soft­ware and tech­nol­ogy.

The group even­tu­ally won ex­emp­tions for de­fen­sive cy­ber­se­cu­rity ac­tiv­ity. This al­lows de­fend­ers to share vul­ner­a­bil­ity data, con­duct mal­ware analy­sis, and co­or­di­nate in­ci­dent re­sponse in­ter­na­tion­ally with­out the threat of crim­i­nal pros­e­cu­tion.

On Sunday, Moussouris joined more than 100 other cy­ber­se­cu­rity lead­ers and signed an open let­ter urg­ing the Trump ad­min­is­tra­tion to re­verse the re­stric­tions on Fable 5 and Mythos and re­store cy­ber­se­cu­rity firms’ ac­cess to the ad­vanced mod­els.

To pull the best ca­pa­bil­i­ties away from de­fend­ers with­out a good rea­son when our ad­ver­saries are rapidly ad­vanc­ing is dan­ger­ous,” they wrote.

In her blog, Moussouris ar­gues that there was no guardrail by­pass or jail­break. Defenders should be able to ask AI sys­tems to find and fix bugs, and write tests to val­i­date the patch, she said. Anthropic’s mod­els were do­ing the most valu­able thing an AI model can do for de­fen­sive se­cu­rity: ex­e­cut­ing the find, fix, and test loop de­fend­ers run every day.”

REG AD

Removing the ca­pa­bil­ity for mod­els to re­spond to de­fen­sive re­quests makes AI sys­tems worse at find­ing bugs and ver­i­fy­ing patches,” she con­tin­ued.

Plus, the US can’t ex­tend ex­port con­trols to open-weight sys­tems or sim­i­lar ad­vanced mod­els from China and other coun­tries - and these sys­tems will soon achieve Mythos-like ca­pa­bil­i­ties, any­way. Anthropic and Google have both ac­cused China-based ri­vals in­clud­ing DeepSeek of us­ing distillation at­tacks” to train their mod­els by si­phon­ing knowl­edge from American com­pa­nies’ AI.

Banning Anthropic’s ad­vanced mod­els is go­ing to hurt de­fend­ers more than at­tack­ers, Moussouris warns. Defense im­proves when de­fend­ers find the same bugs at­tack­ers find and fix them faster,” she wrote. We need the best tools to de­fend against in­creas­ingly ca­pa­ble at­tack­ers in the AI era of cy­ber­se­cu­rity.”

The Register reached out to the Trump ad­min­is­tra­tion for com­ment on Moussouris’ as­ser­tion, and we’ll up­date this post if we hear back. ®

The time the x86 emulator team found code so bad that they fixed it during emulation

devblogs.microsoft.com

During an ex­change of war sto­ries, a col­league of mine told one from back in the days when Windows in­cluded a proces­sor em­u­la­tor for x86 – 32 on sys­tems that na­tively ran some other proces­sor. (This has hap­pened many times. And no, I don’t know which proces­sor this par­tic­u­lar story ap­plied to.)

This par­tic­u­lar em­u­la­tor em­ployed bi­nary trans­la­tion, gen­er­at­ing na­tive code to per­form the equiv­a­lent op­er­a­tions of the orig­i­nal x86 – 32 code. This of­fered a sig­nif­i­cant per­for­mance im­prove­ment over em­u­la­tion via in­ter­preter. You can imag­ine that x86 – 32 is just a byte­code, and the em­u­la­tor is a JIT com­piler.

Anyway, my col­league found that there was one pro­gram that needed to al­lo­cate around 64KB of mem­ory on the stack and ini­tial­ize it. The stan­dard way of do­ing this is to per­form a stack probe to en­sure that 64KB of mem­ory is avail­able, then sub­tract­ing 65536 from the stack pointer, and then ini­tial­iz­ing the mem­ory in a small, tight loop.

But us­ing a loop to ini­tial­ize the mem­ory was too mun­dane for what­ever com­piler was used to com­pile this code. Instead of gen­er­at­ing a loop to ini­tial­ize each byte of the buffer, the com­piler optimized” the code by un­rolling the loop into 65,536 in­di­vid­ual write byte to mem­ory” in­struc­tions, each 4 bytes long.

All in all, it took this pro­gram 256 kilo­bytes of code to ini­tial­ize 64 kilo­bytes of data.

This of­fended the team so much that they added spe­cial code to the trans­la­tor to de­tect this hor­ri­ble func­tion and re­place it with the equiv­a­lent tight loop.

Author

Raymond has been in­volved in the evo­lu­tion of Windows for more than 30 years. In 2003, he be­gan a Web site known as The Old New Thing which has grown in pop­u­lar­ity far be­yond his wildest imag­i­na­tion, a de­vel­op­ment which still gives him the hee­bie-jee­bies. The Web site spawned a book, co­in­ci­den­tally also ti­tled The Old New Thing (Addison Wesley 2007). He oc­ca­sion­ally ap­pears on the Windows Dev Docs Twitter ac­count to tell sto­ries which con­vey no use­ful in­for­ma­tion.

I Love the Computer

michaelenger.com

In a re­cent dis­cus­sion on the Aftermath Podcast about the ill ef­fects of the cur­rent AI hype cy­cle, one of the ed­i­tors said some­thing that re­ally res­onated with me:

I love the com­puter. — Chris Person

I love the com­puter. — Chris Person

This was in the mid­dle of a rant about how these snake oil sales­men are ru­in­ing the space he loves with their in­sa­tiable avarice and, as much as I’d like to add my voice to the cho­rus of tech­nol­o­gists who are le­git­i­mately an­gry at this so­cial crime be­ing com­mit­ted, I’m go­ing fo­cus on that spe­cific quote.

Because I, too, love the com­puter.

In The Beginning

It all be­gan with a cu­ri­ous box that my mother brought home from work. Sometime when I was around six or seven we were liv­ing in Dølihagen, a sub­ur­ban area near Jessheim, it­self a small town in Norway. The area was less pop­u­lous then than it is now, hav­ing bal­looned in size since they opened the new in­ter­na­tional air­port nearby, and my mem­o­ries of it are a sparse mix of play­grounds, muddy fields, and a sea of ho­moge­nous build­ings.

We had moved there af­ter the death of my fa­ther, from a large house my par­ents had build next door to my grand­par­ents, to a small flat on the lower ground floor where my mother, my brother, and I all slept in the same room. My mother flit­ted through a se­ries of work­places and even­tu­ally landed a job in the min­istry of for­eign af­fairs, a po­si­tion that was go­ing to send us to the Philippines. I don’t re­mem­ber much of the prepa­ra­tions lead­ing up to the move, but one ex­pe­ri­ence is burned into my mind: the day she brought home the com­puter. From the mo­ment she un­packed and set it up on the din­ing room table I was en­thralled.

This daunt­ing and for­eign ma­chine was fairly typ­i­cal for the early 90s and was a tool she was given to aid in her new work. It was an IBM 486 DX6 run­ning Windows 3.0 (later Windows for Workgroups 3.11), housed in a busi­ness-grey tower adorned with green LEDs and an be­guil­ing turbo but­ton. It came pre­in­stalled with Paint, SkiFree, and Solitaire, and would be­come my por­tal into a new world wherein I would find friends, hob­bies, and a ca­reer. It was even­tu­ally equipped with a sound card and CD-ROM drive—my mother damn­ing whomever had coined the term Plug & Play” dur­ing its in­stal­la­tion—and I have count­less mem­o­ries of time spent ex­plor­ing all it had to of­fer. Nearly two decades af­ter that first in­tro­duc­tion, a ther­a­pist would spec­u­late that my in­ter­est in com­put­ers could stem from how it was a rare point of sta­bil­ity in a life where I ended up leav­ing my home and my friends every few years.

The Smell of Ink on Cheap Paper

As I had the plea­sure of be­ing drawn into the world of com­put­ers in the pre-In­ter­net era1, my ex­pe­ri­ence of dis­cov­ery is in­ex­orably linked with the en­thu­si­ast print me­dia at the time. Magazines like TEKNO and Geek, and even­tu­ally Incite PC Gaming and PC Gamer, gave me in­sight not only into the hard­ware and soft­ware that so en­rap­tured me, but a cul­ture which I yearned to be a part of. My in­ter­ests re­volved mostly around gam­ing, but I would pour over any and all com­puter-re­lated pub­li­ca­tion I could get my hands on. I un­der­stood very lit­tle but I was des­per­ate to learn, and thor­oughly ex­plored the floppy discs and CDs that came along with the mag­a­zines. Through these pub­li­ca­tions I picked up the slang and the over­lap­ping in­ter­ests, and found my­self build­ing an iden­tity around what it meant to be a geek”, a gamer”, or a computer guy”. Having re­vis­ited some of those mag­a­zines in re­cent times shows how crude, misog­y­nis­tic, and ad­ver­sar­ial a lot of the writ­ing was, so al­though they pro­vided me with a lot of self-dis­cov­ery I’m glad I out­grew that men­tal­ity.

It would be easy to say that it’s just nos­tal­gia that makes me lament what was lost in the tran­si­tion to the Internet, and it’s not like print was spared the rot of cap­i­tal­ism that has made on­line geek spaces into ad-rid­den, en­gage­ment-max­imis­ing cesspools. But I am glad that I was able to do my ini­tial dis­cov­ery in a world de­void of pop-ups, auto-play­ing ads, click-bait, and in­ces­sant pleas to like and sub­scribe”. Print me­dia was slow, im­per­fect, and filled with a cal­lous tox­i­c­ity that still per­me­ates the hobby, but, as I have es­poused be­fore, there is some­thing unique in the kind of writ­ing that was done by a full ed­i­to­r­ial team and meant for per­ma­nently print­ing on pa­per. All that be­ing said, as much as I loved the mag­a­zines I col­lected, they pale in com­par­i­son to the end­less font of knowl­edge which was about to en­ter my life. For what bet­ter place is there for a kid with a vo­ra­cious ap­petite for niche in­for­ma­tion than the won­der­ful World Wide Web?

An Era of Self-Discovery

In my early teens we moved to Malaysia and I started at­tend­ing a school which had a per­ma­nent Internet con­nec­tion avail­able to stu­dents. I had had a brief in­tro­duc­tion to use of the Internet at my for­mer school, but hav­ing it avail­able on all com­put­ers and free to be ex­plored was a boon to a tech ad­dict like me. My ex­plo­ration was wide and deep, and I can re­call read­ing film re­views on GeoCitites sites and play­ing early Java-based games hosted by ISPs. I printed hun­dreds of pages of SNES walk­throughs on the li­brary printer, and down­loaded pix­e­lated pho­tos of naked peo­ple to a col­lec­tion of floppy disks, spurred on by re­bel­lious peers and the rav­en­ous needs of my bur­geon­ing sex­u­al­ity. I went every­where and did every­thing I could.

Magazines were still the pri­mary source of new in­for­ma­tion for many years af­ter this, but brows­ing the Internet be­came my favourite way to spend my free time. It felt as if dis­ap­pear­ing into this dig­i­tal world was giv­ing me a real con­nec­tion to the al­lur­ing cul­ture I so des­per­ately wanted to be a part of, and though I felt lost in life but I found my­self in the Net. I lived close enough by the school to bike over when­ever I wanted and the com­puter labs were open on week­ends, so in­stead of spend­ing time with peers I would spend it hap­pily ex­plor­ing all the kinds of places I had read about in the mag­a­zines.

By the time we moved back to Norway and then fur­ther out to Colombia I had be­come so deeply em­bed­ded into the Internet that I would spend en­tire week­ends in my room by my­self. I ex­plored fo­rums and chat rooms, and toyed with all the things I could get my com­puter to do. At this point I had also thor­oughly sur­passed my moth­er’s tech­ni­cal ca­pa­bil­i­ties and was reg­u­larly switch­ing out com­po­nents that I had re­searched and pur­chased my­self. When a pi­rated CD of Planescape: Torment ex­ploded in my CD drive, the frus­tra­tion was mixed with the glee of get­ting some­thing shiny and new to add to this grey tower that was be­com­ing an ex­ten­sion of my ego.

I had be­come one with the ma­chine, and all my in­ter­ests re­volved around the com­puter. There was only one fi­nal puz­zle piece to add: pro­gram­ming.

Not Exactly a Natrual Talent

My first time pro­gram­ming did­n’t go very well. One of the mag­a­zines I had fea­tured an ar­ti­cle about this new thing called Java that was get­ting a lot of buzz, and I wanted to try out for my­self. Unfortunately, the nec­es­sary com­piler was on the ac­com­pa­ny­ing CD, which had­n’t sur­vived my brother and his friend us­ing it as fris­bee. However, hav­ing had much suc­cess with ex­per­i­men­ta­tion so far I de­cided to try some­thing that would prob­a­bly work: I copied the code from the mag­a­zine into Notepad and saved the file as .exe in­stead of .txt.

You don’t need to have a lot of tech­ni­cal un­der­stand­ing to imag­ine that this ap­proach did­n’t work. Despite hav­ing read the ar­ti­cle mul­ti­ple times I had­n’t quite grasped the con­cept of com­pi­la­tion and, as far as I un­der­stood, I just needed to write the cor­rect in­can­ta­tion into a file and tell the com­puter to ex­e­cute it. In hind­sight it’s funny how close I was to a work­ing so­lu­tion if only I had known about shell scripts, but this was a fail­ure and caused me to not make an­other at­tempt for sev­eral years.

Eventually, thanks to a class in school and some in­dus­tri­ous friends, I be­came well versed in pro­gram­ming. I got fa­mil­iar with Java, tried my hand at C++, and dove into PHP when that hit the scene. By the time I grad­u­ated from the International Baccalaureate pro­gramme I had re­ceived an ho­n­ours in Computer Science, nat­u­rally mov­ing on to tak­ing a Bachelors in Computer Science from the University of Oslo. It’s been many years since I first tried to re­name a text file to an ex­e­cutable, and since then I’ve made dozens of pro­grams in a myr­iad of dif­fer­ent lan­guages2. I’ve had jobs rang­ing from as­sem­bly line web­site de­vel­op­ment3 to work­ing on au­dio play­back en­gines and I still en­joy slinging code”, al­though maybe with a lit­tle less gusto than I once had. During my uni­ver­sity days I spent so much time mak­ing web­sites and games that it re­sulted in me fail­ing a lot of my classes, so there is clearly a love there which has kept me go­ing.

A Life Well Lived?

Computers have been a large part of my life since that fate­ful child­hood en­counter with a mys­te­ri­ous and noisy beige box. My un­der­stand­ing of its in­ner work­ings and the cul­ture around it did a lot to shape my in­ter­ests and per­son­al­ity in my for­ma­tive years, and I doubt I’d be the same per­son if I had­n’t had this as my main in­ter­est for so long. Now as I am slid­ing gra­cious­ly4 into mid­dle-age I can see the ef­fect it has had on me, and I can con­fi­dently say that I have no re­grets. Considering the com­fort­able and in­vig­o­rat­ing ca­reer I got out of my pas­sion, I would also say that get­ting into com­put­ers in the early 90s was a re­ally smart move.

But things feel dif­fer­ent now. I can re­late to what Chris Person said when he ex­pressed his frus­tra­tions about how these slick con­men are us­ing the tech­nol­ogy I adore as tools for ex­ploita­tion and dis­em­pow­er­ment. The Internet, built by ide­al­ists on a foun­da­tion of open­ness and com­mu­nity, has be­come a mire of dark pat­terns and gar­dens with ever thicker walls, des­per­ate to keep peo­ple within an ecosys­tem where their at­ten­tion is the prized com­mod­ity. I’ve wit­nessed a nerdy space full of nerds be in­vaded by mar­keters, cal­lous cap­i­tal­ists, and brogrammers”—exaggerating the worst, most toxic, as­pects of geek cul­ture in their pur­suit of money and power. I’ve poured hun­dreds of hours of work into open source pro­jects only to have it all be scraped into a pla­gia­rism ma­chine and then ag­gres­sively sold back to me. It feels that the hope I had for the fu­ture tech­nol­ogy could give us, the naïve and starry-eyed fan­tasies I fos­tered in my youth, has been eroded when faced with a re­al­ity where the thing I love can make a lot of money for peo­ple who don’t care for any of it.

Then again, it’s not all bad. We’ve come a long way from the time when com­put­ers were seen as ex­pen­sive and ex­clu­sive tools, and the un­wel­com­ing do­main of elit­ist men. Programming—with the em­pow­er­ment that it brings—is more ac­ces­si­ble than ever and there seems to be a strong cul­tural shift in the techie spaces away from cen­tralised ser­vices and onto fed­er­ated, self-hosted, and in many other ways more free al­ter­na­tives. The Internet seems to be­com­ing more and more locked down, but us weirdos will just stay in our weird cor­ners and will find means to cir­cum­vent any re­stric­tions put on us. My af­fec­tion to­wards tech­nol­ogy made me an os­tracised out­sider when I was younger, then it con­de­scend­ingly made me into a rockstar”, and now it’s look­ing like my peers are ush­er­ing in the end of civilised so­ci­ety. So I’m ready to go back into be­ing just some strange guy with strange in­ter­ests, do­ing silly things peo­ple don’t un­der­stand and don’t care to.

Because, man… I love the com­puter.

It had def­i­nitely around, but was by no means as ubiq­ui­tous as it is now. ↩︎

It had def­i­nitely around, but was by no means as ubiq­ui­tous as it is now. ↩︎

Check out my Codeberg pro­file if you want to see some of them. ↩︎

Check out my Codeberg pro­file if you want to see some of them. ↩︎

Otherwise known as being a con­sul­tant in a dig­i­tal agency”. ↩︎

Otherwise known as being a con­sul­tant in a dig­i­tal agency”. ↩︎

Debatable. ↩︎

Debatable. ↩︎

SpaceX overtakes Amazon to become world's fifth most valuable firm

www.bbc.com

3 hours ago

Archie MitchellBusiness re­porter

Reuters

Elon Musk’s SpaceX has over­taken Amazon to be­come the world’s fifth most valu­able com­pany af­ter a surge in its share price.

Days af­ter join­ing New York’s tech-fo­cused Nasdaq stock ex­change in the biggest pub­lic list­ing ever, its share price has risen by more than 50%.

It leaves Musk’s rocket com­pany worth about $2.78tn (£2.1tn), while Jeff Bezos’s sprawl­ing on­line re­tail and me­dia em­pire is cur­rently worth about $2.66tn.

The boom in SpaceX’s value came as it an­nounced it was buy­ing AI cod­ing start-up Cursor for $60bn.

SpaceX said it would take over Anysphere, Cursor’s par­ent com­pany, which makes the ar­ti­fi­cial in­tel­li­gence cod­ing agent.

Soaring shares

SpaceX has gar­nered huge en­thu­si­asm among in­vestors for its vi­sion of send­ing AI data cen­tres to space and even help­ing hu­mans to colonise Mars.

Its list­ing raised $85.7bn and minted Musk as the world’s first tril­lion­aire. Since first sell­ing shares to the pub­lic at $135 each on Friday, they have risen to $209.

But an­a­lysts have ques­tioned the sus­tain­abil­ity of its high share price given the huge amount of un­cer­tainty over its fu­ture earn­ings.

While Amazon is a house­hold name, with its brand dif­fi­cult to avoid be­ing en­coun­tered on an al­most daily ba­sis, SpaceX is less em­bed­ded in the lives of the gen­eral pub­lic.

Despite SpaceX’s stock mar­ket value over­tak­ing Amazon, the rev­enues and prof­its made by the com­pa­nies are vastly dif­fer­ent.

Amazon made $30.3bn of profit in the first quar­ter of 2026, while Musk’s fu­ture-fo­cused SpaceX lost $4.3bn.

In 2025, Jeff Bezos’s firm ac­crued some $716.9bn in sales, while SpaceX recorded $18.67bn.

But in­vestors ap­pear to be bet­ting on what they think SpaceX can acheive. While its biggest fo­cus is the man­u­fac­ture and launch of rock­ets with reusable parts, the com­pany also man­u­fac­tures and launches Starlink in­ter­net satel­lites, and is ramp­ing up its pres­ence in the AI race.

SpaceX and Cursor have been part­ners since April, when Musk’s firm an­nounced it had the right to ei­ther buy it for $60bn, or pay $10bn for the work they have done to­gether.

Like OpenAI and Anthropic, Cursor’s tech­nol­ogy uses AI to au­to­mate the process of writ­ing code, one of the most promi­nent cur­rent uses for ar­ti­fi­cial in­tel­li­gence.

The tie-up comes as SpaceX tries to catch up with ri­vals by grow­ing its AI busi­ness, xAI, which is be­hind the con­tro­ver­sial Grok chat­bot.

Announcing the part­ner­ship in April, SpaceX said: The com­bi­na­tion of Cursor’s lead­ing prod­uct and dis­tri­b­u­tion to ex­pert soft­ware en­gi­neers with SpaceX’s mil­lion H100 equiv­a­lent Colossus train­ing su­per­com­puter will al­low us to build the world’s most use­ful mod­els.”

Cursor is used by ma­jor com­pa­nies in­clud­ing Stripe, Adobe and Nvidia, whose boss Jensen Huang has de­scribed it as his favourite en­ter­prise AI ser­vice”.

SpaceX said the deal would be com­pleted by the end of September, with Cursor’s share­hold­ers paid with $60bn worth of SpaceX shares.

Just a moment...

gmalandrakis.com

Apple’s weird anti-nausea dots cured my car sickness

www.theverge.com

I’ll just work from the car, I thought. But af­ter a few min­utes of star­ing at my screen on quick moun­tain switch­backs I could feel the first signs of cold, co­ag­u­lated nau­sea bub­bling up from that sweaty place in my gut. I looked to the hori­zon for re­lief, but noth­ing helped… un­til I re­mem­bered Apple’s magic dots.

Introduced in 2024, Apple’s Vehicle Motion Cues promise to tap into your de­vice’s ac­celerom­e­ter and gy­ro­scope to re­duce or, in my case, even elim­i­nate the mo­tion sick­ness felt when try­ing to use an iPhone, iPad, or MacBook in­side a mov­ing ve­hi­cle.

According to big-S Science, this type of ve­hi­cle mo­tion sick­ness is caused by the eyes star­ing at a sta­tic dis­play while the in­ner ear feels the car turn­ing, brak­ing, and ac­cel­er­at­ing. Motion Cues solve this by plac­ing dots around the pe­riph­ery of the dis­play that move in har­mony with the mo­tion of the car. When the car turns right, the dots sweep across the screen to the left; when the car brakes the dots slide for­ward.

It sounds pre­pos­ter­ous, but I’m here to tell you that it ac­tu­ally works. Once en­abled, I’ve com­fort­ably read books in the Kindle app on my phone for a few hours at a go, and even writ­ten 1,000-word re­views while my wife drove our camper van to the next des­ti­na­tion. She uses Apple’s Vehicle Motion Cues now, too, be­cause they’ve been a game changer for how we bal­ance work with life on the road.

Vehicle Motion Cues can be con­fig­ured un­der ac­ces­si­bil­ity set­tings in iOS, iPa­dOS, and ma­cOS. They can be turned on, off, or set to ap­pear au­to­mat­i­cally when ve­hi­cle mo­tion is de­tected. I pre­fer to tog­gle the dots to avoid see­ing them when I’m dri­ving the car. The black dots are fairly un­ob­tru­sive, but they can in­ter­fere with maps, text, and im­agery on long straight stretches of road that cause the dots to sit mo­tion­less (Apple should dim all the dots in those sit­u­a­tions). You can also con­fig­ure the dot size, color, and den­sity if you want, but I found the de­faults to work just fine.

I made it easy to quickly tog­gle the Motion Cues on and off by dou­ble tap­ping the back of my iPhone. To do the same, head over to Accessibility –> Touch –> Back Tap and set the Double Tap ges­ture to Vehicle Motion Cues on de­vices sup­port­ing iOS 18 and above.

I’m for­tu­nate that I re­mem­bered this ob­scure ac­ces­si­bil­ity fea­ture that I used al­most daily on a re­cent two-month road trip around Europe. Hopefully you’ll find sim­i­lar suc­cess when trav­el­ing this sum­mer.

Follow top­ics and au­thors from this story to see more like this in your per­son­al­ized home­page feed and to re­ceive email up­dates.

Thomas Ricker

To add this web app to your iOS home screen tap the share button and select "Add to the Home Screen".

10HN is also available as an iOS App

If you visit 10HN only rarely, check out the the best articles from the past week.

Visit pancik.com for more.