The Fuzzer at the Coffee Machine

Posted: October 15, 2026

author-image

By

Authors:

Eric Alata, Dr. Ben Gras, Benoît Morgan, and Neer Roggel

This blog post walks through how we fuzz embedded firmware codebases, and the fuzzer features we have developed and deployed along the way. The fuzzer presented here relies on a simulator to execute the firmware in a controlled environment, observe its behavior, and repeatedly exercise the target with generated inputs. The taint handling mechanism helps identify which input bits influence execution. We use that information to generate better initial corpus entries (also known as seeds), so that the fuzzer starts from inputs that already exercise meaningful firmware behavior.


This post is intended for firmware, validation, and security engineers who are evaluating how to adapt fuzzing to simulation-based embedded environments. It is not a tutorial on fuzzing internals, but it explains several implementation choices at a practical level.

To make the fuzzer results easier to investigate, additional tooling was added to analyze execution traces generated from inputs that crash the system. This makes it easier to understand what happened, reproduce the issue, and connect a failing input to the behavior it triggered inside the firmware.

Ultimately, this fuzzer is intended to be used in SoC environments that contain multiple execution units, where firmware behavior depends on complex interactions between components.

Rather than presenting the work as a conventional technical report, we chose to tell it as a story: a dialogue between characters inspired by recurring roles and questions in the security industry. This format reflects how the project took shape: through discussions, constraints, and iteration.

The goal is not only to describe the final tooling, but also to show the reasoning that led to it. Fuzzing firmware is rarely a straight path, and many of the most important design choices only become clear when engineers challenge assumptions, compare trade-offs, and adapt to the realities of the codebase.

The characters are fictionalized. What remains is the story of the technical journey.

Day 1: Coffee

AI-generated image courtesy of Ben Gras. Image generated using ChatGPT.

 

The office was still quiet.

Sam, the company's security lead, was having coffee with Dave, one of the developers. They were already in the middle of a conversation about firmware validation.

"...and that is why security matters," said Sam. "Not as a ceremony at the end, but as a way to understand what the firmware can really do."

Dave, a developer, nodded. "Especially when the firmware is not a single program sitting in one process."

Polly appeared and joined them. "Sorry to interrupt," she said. "I heard the word security. I am Polly. I lead a product team. I have a question. We are starting a low-level firmware project, and I would like support from the security team. But I need to begin with the minimum viable setup. I can't allocate six months to this."

Sam looked at Dave, then back at Polly. "You should look at fuzzing."

"The idea is simple," said Sam. "You do not test only the expected inputs. You generate many inputs, execute them, and look for behavior no one planned to explore. You’ll get crashes, hangs, and other undesirable states."

Polly leaned on the table. "That sounds like what I need. But where do I start?"

"A good fuzzer for a project obviously depends on what it runs against," said Dave. "Your target is low-level firmware, not a classic application target. It is close to hardware and often distributed. Timing and state of the whole system matter. Small changes in order can change the result. And when something fails, you may not get a clean stack trace. You may get a hang, a livelock, a bad state, or two traces that only make sense together. The fuzzer must be able to detect those situations, not only ordinary crashes. Standard application fuzzers do not fit this distributed firmware environment directly."

Polly stayed silent for a moment. "So fuzzing is useful for firmware. But a normal tool may not fit directly. Okay. Let’s invite Integration Ian. Then we can talk about what a useful fuzzer would really need."

Day 2: Choosing the Execution Environment

That night, Dave stayed late with Sam. The coffee machine was off, but the whiteboard was on. "If we want to run a fuzzer," said Dave, "we first need somewhere to execute the firmware. Polly's firmware will eventually run on a real target. We could use that."

Sam shook his head. "We can’t run this on bare metal. It’s still being built, and besides, we want to run many sessions in parallel, and we need trace-level visibility. "

Dave wrote four lines on the board [FuzzStrat].

Table 1: Fuzzing Strategy

ApproachLow EfortHigh FidelityHigh Speed/ScalingHigh Visibility
Real platform✓✓✓X
Emulation or simulationX✓X✓
Rehosting and refactoringXX✓✓
Virtualization, kAFL-style/QEMU✓X✓✓

"We could rehost the firmware," said Dave. "Port it, mock the hardware dependencies, maybe even transpile parts of it into an x86 binary. Then we could use standard fuzzers more directly: AFL++, libFuzzer, honggfuzz, tools like that."

Sam disagreed: "Polly and Ian will have to live with that. We risk diverging the rehosted code and the real product; and the product team has to maintain the fork." "Okay. Virtualization gives a cleaner fuzzing loop," said Dave. "kAFL and Nyx showed how useful snapshots, resets, and coverage feedback can be. QEMU-style execution is easy to extend and mock in the direction we want."

"But only if the target fits that world," said Sam. "Here, the SoC behavior and the firmware environment are embedded and product-specific. If we have to model too much, we are back to maintaining a fork."

Dave looked at the second line again. "Okay. Let’s lean on their simulation environment then. They already use it for validation. Ian's team already knows how to run tests there. It preserves more of the real firmware flow. This slide illustrates roughly how they operate their simulator for functional validation."

Firmware Simulation Process (SIMICS)

Figure 1: High level validation architecture

 

"It will be slow," said Sam.

"A bit," said Dave. "But the literature has already attacked that problem with parallelization. kAFL-style fuzzing gives us a useful reference model: controller, workers, snapshots and, when possible, replay, corpus management, and feedback-guided scheduling. We cannot copy it blindly, but we can borrow these ideas."

Sam nodded. "So the note to Polly should not say that one existing fuzzer solves the problem. It should say that existing fuzzers give us the pieces: mutation, coverage, corpus management, snapshots, scaling. The execution environment should be the developers’ simulation flow, because that promotes adoption and fidelity."

"Agreed," said Dave. "I will read the kAFL paper again and propose a first cut architecture that we can show them tomorrow."

Before midnight, Dave sent Polly a short note. It only recommended a direction: use the existing simulation environment as the execution backend, design the fuzzer around workers and replay, and borrow the proven ideas from AFL-style and kAFL-style fuzzing without pretending that a standard userland fuzzer could be dropped onto their firmware unchanged.

Day 3: The Real Meeting

The next day, the four of them met in a small room.

Ian had a laptop open.

Polly had printed Dave's note. Sam had copied the comparison table on the whiteboard.

“Dave and I think the direction that fits best is to piggyback on your environment. We could take other directions, but this one has a strong advantage: it does not change the way your team already works, it can fit into the development process, and there is good product fidelity." Dave stood up, connected his laptop, and brought the architecture diagram onto the screen.

Figure 2: High-level architecture of the fuzzer

 

"The first component is the controller,” said Dave. “It starts the campaign and manages it: seeds, mutations, scheduling, results, and stopping conditions. To run the campaign, it relies on workers. The number of workers is configurable. Each worker is attached to one instance of your simulation environment. If we need more throughput, we will increase the number of workers, as long as the infrastructure can carry it."

Ian nodded slowly. "I see the idea. But do we have to restart the simulator for every new input? It’ll take forever."

"We saw that you already use test scenarios to drive simulations," said Dave. "Our idea is to piggyback on that model. The simulator runs normally. When the scenario reaches the moment we care about, the worker takes control and starts submitting fuzzing inputs."

"Exactly," said Dave. "So: how about snapshotting? If we snapshot program state at the precise moment fuzzing starts, then we can test many inputs from the same baseline state. But, in practice, many simulators use multiple threads or processes, so that snapshotting may require tools like CRIU and careful integration. Without snapshots, the next input may run on a contaminated state modified by previous inputs. Choosing to avoid implementing snapshots keeps the first integration simple. However, this choice has a cost: failures can become order-dependent, meaning that the order in which inputs are provided changes the outcome. For this first version of the fuzzer, the harness must either restore the relevant firmware state between inputs or else log enough context to replay state-dependent failures. At the moment, snapshotting remains a future improvement, because it would give cleaner isolation and faster reset."

Polly did not hesitate. "For now, let us start with the simplest path: no snapshots. We can ask for that later if the first version proves useful."

She looked back at the diagram. "I understand the architecture, but how do you choose the values to submit? Our input data usually has a fixed size, if that helps."

"It helps a lot," said Dave. "We can use the classic seed and mutation approach. Seeds are the initially submitted values. Then mutation takes a value that was already submitted and changes parts of it. It is close in spirit to genetic algorithms: we keep producing variants, but we give more weight to the ones that teach us something."

"What kind of weight?" asked Polly.

"A score," said Dave. "If a mutated value discovers new firmware behavior, its score goes up. If it only repeats something we already know, it is less interesting."

Ian leaned forward. "How do you detect behaviors? And more importantly, how do you catch bugs?"

"Because we are inside a simulator, we can collect the program counter (PC)," said Dave. "From the collected PCs, we derive a coverage signal: for example, basic blocks visited, edges traversed, or a normalized trace hash. Inputs that expand this coverage are treated as interesting and kept in the corpus. Regarding bugs, you already have firmware and simulator events to handle catastrophic situations: assertions, error states, hangs, protocol failures, and similar signals. We plan to hook into those events instead of designing separate detectors to check for bad behavior."

Ian pointed at the diagram.

"It separates the fuzzer core from your simulation environment," said Dave. "The controller, corpus logic, scoring, and workers belong to the fuzzer. The simulator, test scenario, firmware, and product-specific hooks stay on your side of the shapes. The plan is to package our fuzzer as a Python module, so that it can later be reused for other firmware targets, possibly with other simulation environments. This is decoupling."

"That matters," said Ian. "I do not want a tool that becomes too entangled with one product setup."

Sam nodded. "Let’s build a first small version. Run several workers, submit fixed-size inputs, and report failures you already detect."

Ian became pensive. “Before we do that, one last question. What is pfuzz? What does the “p” stand for?” Sam responded triumphantly, “Oh, that. Simple. It’s up to you.”

Day 4: First Release

A week later, they met again in the same room.

"I have been able to build the first prototype,” said Dave. “Using Intel® Simics® as the execution environment made the job much simpler, because most of the control and observation features we need already exist."

"Can you walk us through a demo?” said Ian. “I hope I can integrate this into CI.”

"Sure," said Dave. "Our code is small for now. A controller, a worker, and the glue needed to talk to the simulator. On the simulator side, I take one of your existing test scenarios and turn it into a harness. When the worker launches the simulator, it also tells the simulator to use that harness. The concrete fuzzer architecture looks like this.” Dave shared the slide and continued. “From there, I need a concrete example."

Figure 3: Concrete fuzzer integrated into customer’s validation environment

 

"I am currently integrating this function," said Ian. He turned his laptop so everyone could see the screen. "It executes a four-byte message read from IO space and acts as a relay towards another firmware component. Can we use it as an example?"

int f(uint32_t message) {
    if ((message & 0xffff) == 0x1020) {
        out(CMD, message);
        return SUCCESS;
    } else if (message + 0x1231 == 0x099876) {
        out(CMD, message);
        ABORT();
    }
    ASSERT(message < 128);
    return ERROR;
}

 

"No problem," said Dave. "The harness only has to send the IO message with the value provided by the worker. Then, as soon as function f starts executing, we begin collecting PCs. We know its address from debug data (DWARF). We stop collecting when the function returns."

The controller started two workers. Each worker launched one Intel Simics instance, waited for the scenario to reach the IO message, and began sending fuzzing values.

After a few seconds, the counter stabilized around forty tests per second. Three minutes later, the terminal printed CRASH. The counter dropped to twenty tests per second, then slowly climbed again.

workers: 2

...

timestamp 01: exec/s: 40

timestamp 02: exec/s: 40

timestamp 03: exec/s: 40

timestamp 04: exec/s: 40

...

worker 02: CRASH input = 80 00 00 00

worker 02: restarting simulator

timestamp 12: exec/s: 20

timestamp 13: exec/s: 20

timestamp 14: exec/s: 20

timestamp 15: exec/s: 40

...

"What happened?" asked Polly. “Wait... you found a crash?"

"Yep," said Sam. "The crash is stored in a log alongside the input that triggered it and the context needed for debugging."

Polly looked at Ian's function again. "This crash corresponds to the ASSERT(message < 128) code path, right? It seems that this function may have more than one issue though. This first one is visible now. How do we know if there are others?"

"We have to give time to the fuzzer," said Sam. "The longer it runs, the more confidence we gain. But even with a good campaign, fuzzing does not prove the absence of bugs."

"Good," said Polly. "Then let us move on to integration into CI."

Day 5: Toward Better Coverage

A few days later, Sam was alone in the meeting room.

Ian's function was still written on the whiteboard. Sam had circled the comparisons and written 40 exec/s in the corner.

Dave entered the room with a coffee. "Are you looking for bugs by hand?"

Sam smiled. "No. I am looking for a way to make the fuzzer find them faster. Mutation alone may take a long time to discover narrow input regions such as “message & 0xffff == 0x1020”. Taint analysis helps identify which input bits affect each branch. Once we know that, we can generate candidate inputs that exercise branch outcomes more deliberately."

Dave looked at the code. “The fuzzer already knows when a trace corresponds to new behavior. The question is why it does not discover all behaviors quickly.”
"Oh, I see,” remarked Dave. “The complex condition is really hard to solve for automatically."

"Yes," said Sam. "Even with mutation, the fuzzer really has to get lucky. Like winning a lottery. The question is how to activate that smaller region. I was thinking about taint. But before that, show me the assembly generated for this function."

Figure 4: 32-bit x86 (i386) disassembly of f(), with C source interleaved

 

"Here it is," said Dave. "Why were you talking about taint?"

"At C level, we detect behaviors by looking at control structures," said Sam. "What are we looking for at the assembly level, then?"

"Conditional jumps," said Dave.

"Exactly. The idea is to observe how the initial input values influence those jumps by propagating a label, or color, corresponding to part of the input, through execution."

"I see," said Dave. "Since we only manipulate four bytes of the input message, shouldn’t four colors be enough?"

"No," said Sam. "That is too coarse. The first condition is narrow because only one value out of 65,536 lower-half values matches. The second is also narrow, and reaching it requires satisfying the previous branch outcome first. With one color per byte, we would never correctly compute the branch condition over the 32-bit value. And firmware uses masks and packed fields all the time. If we track only bytes, we lose that distinction."

"So you want bit-level taint," said Dave.

"Yes," said Sam. "In this example, we would have thirty-two colors: one for each input message bit. As you can see on this slide, a taint is not always just a color. It can also be fixed to zero or one, for example when it comes from a constant in the code. It could be unknown when the analysis has lost precision. Each time the simulator executes an instruction, the taint engine updates the abstract taint state according to the semantics of that instruction."
 

Table 2. Bit-wise taint domain.

Taint Domain
ColorFixed ValueUnknown
Input message bit 0 up to 310, 1

 

Sam stopped for a second. "How complex will the taint mechanism design have to be?"

"Give me an example," said Dave. "Then I can tell you."

"At the beginning, nothing is tainted," said Sam. "Taints appear progressively as we execute instructions over the abstract state. A color means that the corresponding input message bit may influence that colored data bit."

Dave looked at the assembly listing. "Does the taint state follow the machine state?"

"Exactly," said Sam. "Let us take a few instructions from the code."

  andl $0xffff, %eax
  for bits 0..15:
      taint(eax[i]) keeps its previous abstract value
  for bits 16..31:
      taint(eax[i]) becomes fixed zero
  flags are recomputed from the abstract output value
  ZF is influenced by the abstract value of all output bits

"The andl instruction is interesting because the constant can erase dependencies," said Sam. "The lower sixteen bits keep their taints. The upper bits become fixed zero. They no longer depend on the message. That is exactly why we need more than colors: fixed values are useful information." The engine propagates this abstract state through supported instructions. When it cannot model an operation precisely, it conservatively combines dependencies, marks the result unknown, or stops analysis for that path.

"The idea is to prepare better seeds," said Sam. "We run the harness once with an arbitrary value while computing taints. On the trace we get, we identify conditional jumps where only one side was explored. Then, thanks to the propagated taints, and the built-in solver, we can identify and use the input bits that have influenced the jump to build a new value that should activate the other side. We will gradually grow coverage of the control flow graph.”

"So instead of waiting for the right mutation to guess that the lower sixteen bits of message must be 0x1020, we ask the branch which bits matter?" Interrupted Dave.

"Exactly. Implementation-wise, relying on Intel Simics architecture, this should be straightforward. The branch instructions are explicit enough: jump if greater, jump if equal, register against register or constant. From the concrete values and the taints, we can often infer the input region that reaches the jump or the fall through."

Dave looked back at the brute-force calculation. "I see it now; this is a preparation phase before the fuzzing campaign."

"Yes," acknowledged Sam. "It does not replace fuzzing. It is complementary as it only reasons about control flow. Let us note that this is possible here because the input size is fixed and because the simulator lets us trace execution instruction by instruction. The taint analysis phase is meant to provide the initial set of seeds used to initiate the fuzzing phase, preventing the fuzzer from going down a rabbit hole. In other words, the fuzzer is less biased towards the largest input regions. During the fuzzing phase, the fuzzer still has to mutate these inputs and detect bugs such as overflows, bad states, protocol failures, crashes, etc."

Once the implementation was complete, Dave demonstrated the effectiveness of the introduction of data tainting to generate the fuzzer initial seeds.

Before the introduction of data tainting, the fuzzer was already smart enough to deduce most branch conditions, but couldn’t get to reach some of the most important blocks:

Dave showed in a second slide how the fuzzer ramped up very fast to a 100% coverage using the set of seeds generated out of the data tainting phase. Dave pointed out that with the tainting enabled, lines 27-29 are reached by 3 inputs.

"Great progress," said Sam. "We’re not claiming that we solved fuzzing, but: on a small firmware input space, bit-level taint can identify which input bits control which low-level branches. From there, we can recover the main execution paths, treat them as distinct behaviors, and derive one seed for each path. The fuzzer then starts with a corpus that already covers those behaviors, instead of spending its first runs discovering them by chance. This makes for a more efficient state space search."

Day 6: Second Release

Two days later, they met again with Polly and Ian.

Sam stood next to the whiteboard. "We thought about a way to make the fuzzer smarter," he said.

Figure 5: Phase 2 implementation of the fuzzer

Ian looked at the diagram. "I have the impression that this does not change the integration. I see that seeds are passed into the fuzzing phase. But what exactly is a relevant seed?"

"It is an input that activates one branch of the function you care about," said Sam. "Ideally, we want seeds that cover important branch outcomes and representative paths through the function. Dave, do you have the result of the computation?"

"Yes," said Dave. "First, the runtime is reasonable. Of course, it depends on the initial message or function that we target, but here a few seconds are enough. The pass runs the harness, tracks taints instruction by instruction, observes conditional jumps, and proposes concrete inputs. For don't-care bytes, I used zero."

Figure 6: Control flow graph and taints

 

He wrote the generated seeds on the board with the corresponding graph.

"Then mutation has a better starting point," said Dave. "We still keep coverage as a reward. We still keep crashes, hangs, bad states, and new low-level behavior. But the first corpus is no longer random or empty. It contains representatives for the branch structure we observed."

Dave started the second release of the fuzzer. This time the controller loaded the generated seeds before starting mutation.

After a few seconds, the first crash appeared. It was the same assertion as before.

worker 01: CRASH input = 80 00 00 00

worker 01: reason = ASSERT_FAIL

A few seconds later, a second crash appeared.

worker 02: CRASH input = 45 86 09 00

worker 02: reason = ABORT

"Good," said Polly. "Can we replay these crashes based on the logs?"

"Yes," said Dave. "You just have to take the input that triggered the crash and run it again in the harness."

Ian immediately proceeded. He duplicated the harness, inserted the payload, and relaunched the simulation with all logs enabled. The crash deterministically happened again. Ian opened the logs, followed the events around the fault, and two minutes later identified the bug location.

"What about the debugging effort?" asked Sam. "I think we can give them a tool that makes that work easier. We have to give them a way to see what happened leading up to a crash. Can you please look into it?"

Day 7: Visualization

"I have one last concern," said Polly. "If the fuzzer finds a bug, who is going to explain it to my team?"

"That is the right question," said Sam. "In a distributed firmware system, a crash alone is not enough."

"A raw log will not save this project," said Ian. "If triage is too slow, the tool will never be adopted. People are busy.  They need an actionable report that can be analyzed fast. The simulator will really just save an endless list of executed instruction locations, which is not very human readable. To analyze the bug, we need to know how the crash came to be and where in the original C source the bug resides - that sort of an analysis."

"This is why we need a visualization layer," said Dave. "Right now we call it xtrace."

"What does it show?" asked Polly.

"An execution trace over time," said Dave. "Registers. Key state changes. Events on both sides of the system. The path that led to the fault. The context around the fault. It starts with a raw instruction stream and collapses irrelevant paths as much as possible, leaving the user with a high-level view of the execution. One can navigate to any point in the execution trace, instruction-wise or by function granularity, and at any point you can observe the call stack. Instruction traces can be collapsed to function-level traces as well."

"So not only the final crash point," said Polly.

"Exactly," said Dave. "The final point is often too late and often depends on a side effect that happened much earlier. We need the path. Like reverse debugging."

"And we need to be able to record multiple traces and align them in time," said Sam. "If multiple firmware executions interact in a distributed way, the report must be able to show multiple traces and debug them together. One isolated trace can hide the real cause."

"Can this also help normal debugging?" asked Ian.

"Yes," said Dave. "That is another reason it matters. The same trace can help after fuzzing."

"Then visualization is not just a nice extra," said Polly. "We really need it as part of the adoption."

"It is part of psychological acceptability and trust," said Sam. "People trust a tool when they can understand its findings. This tool helps developers to more easily reproduce the bug, which ensures that developers can trust fuzzer findings. Let me show you."

Day 8: Closing

By the end of the month, the four of them met one last time at the coffee machine.

There was no diagram this time, just reminiscing.

"We built a fuzzer," said Dave. "But a specialized one. The basics are the same as in the literature: seeds, mutation, coverage, crashes, replay. Those ideas still matter. What changes is how we adapt them to the target."

"That is the important part," said Sam. "Security tooling is never completely abstract. The useful improvements depend on the system: the input format, the simulator, the traces, the failure modes, the engineering flow, and the people who will have to use the result."

Ian nodded. "And in this case, the tool fits the way the product team already works. It runs on the simulation environment, it is integrated into the CI pipeline, it can replay findings, and it gives developers something they can investigate."

"It is also not one-off," said Dave. "We designed clear boundaries between the fuzzer core and the target environment for that reason. The campaign logic stays clean; the product-specific harnessing stays where it belongs."

Polly looked at Sam. "In any case, we adopted it, and we will keep using it in our team. Will you deploy it elsewhere?”

"Yes," said Sam. "Thanks to the flexible design, we've already started doing that."

"And it will keep changing," said Dave. "Better taint support, better scheduling, better snapshots, better reports and improved visualizations. A useful fuzzer is not finished once it finds the first crash. It improves as we learn what matters for the target."

Sam smiled. "Good point. Security is not a handoff, but a collaboration. The security team brings methods and threat thinking. The product team brings the target, the constraints, and context. The fuzzer works well because both teams shaped it together."

 

Conclusion

For teams building similar fuzzing infrastructure, the many design questions include: Where can the firmware execute with enough fidelity? How will fuzzing inputs be injected? Can developers inspect the path that led to the failure? The specific answers will differ by product, but these and other discussed questions shaped the design of P-FUZZ and are useful starting points for other firmware fuzzing efforts.

End Note

P-FUZZ is an internally developed project intended to cover a wide variety of firmware targets, while remaining agnostic to the simulation environment. Intel® Simics® system simulator is a publicly available simulation environment that the fuzzer does support, alongside other internal ones. Although P-FUZZ will not be available to the public, Target Software Fuzzer For Simics (TSFFS) is a very capable open-source fuzzer, tailored for Simics (x86, RISC-V and ARM). Readers are invited to experiment with it if they plan to use Simics as a system simulator for their firmware [TSFFS].

 

Bibliography

[FuzzStrat] Credit: Matthias Schunter, Steffen Schulz Intel Labs

[kAFL17] Sergej Schumilo, Cornelius Aschermann, Robert Gawlik, Sebastian Schinzel, and Thorsten Holz. 2017. kAFL: hardware-assisted feedback fuzzing for OS kernels. In Proceedings of the 26th USENIX Conference on Security Symposium (SEC'17). USENIX Association, USA, 167–182.

[BitTaint14] Babak Yadegari and Saumya Debray. 2014. Bit-Level Taint Analysis. In Proceedings of the 2014 IEEE 14th International Working Conference on Source Code Analysis and Manipulation (SCAM '14). IEEE Computer Society, USA, 255–264. https://doi.org/10.1109/SCAM.2014.43

[TSFFS]. Intel. Tsffs. https://github.com/intel/tsffs/, 2023.

 

About The Authors

Eric Alata is an Offensive Security Researcher at Intel Corporation and a member of INT31. His work focuses on hardware and firmware security, combining practical security tooling with formal methods and analysis techniques. Before his current position, Eric held a tenured academic position at INSA Toulouse and conducted research with CNRS. He holds a Ph.D. from the University of Toulouse and a habilitation to supervise research (HDR). His research interests include hardware and firmware security, vulnerability analysis, formal approaches, and the design of tools to support security research.

Dr. Ben Gras is an Offensive Security Researcher in Intel's INT31 team. His primary research topic is helping humans find and diagnose software and hardware vulnerabilities painlessly, by spending computer time. The author received a PhD in systems security from the VU Amsterdam.

Benoît Morgan is an Offensive Security Researcher at Intel Corporation and a member of INT31. His work focuses on microarchitectural vulnerability research, exploit engineering, offensive security tooling, and the security of complex hardware and software architectures. Prior to joining Intel, Benoît was a security researcher at Airbus Operations and an assistant professor at INP-ENSEEIHT, University of Toulouse. He holds a Ph.D. from the University of Toulouse and an engineering degree in Computer Science and Networks from INSA Toulouse. His research has encompassed hypervisor security, DMA and IOMMU vulnerabilities, remote attestation, and hardware-assisted security architectures.

Neer Roggel is a security researcher in Intel's INT31 team, leading client platform security research. Neer is a passionate and seasoned researcher, with 20+ years of experience hacking and defending an assortment of systems. At Intel, he has led red teams to mitigate architectural and implementation vulnerabilities in a variety of security technologies, including Intel® Trusted eXecution Technology and confidential computing technologies. In his current role, he tackles reported breaches and simulates threat actors to discover and exploit deep vulnerabilities, ultimately driving preemptive, principled, and pragmatic defenses into Intel platforms. His current focus is on power management security and firmware hardening, both continually evolving topics. Neer’s background is in securing critical infrastructure (energy, water, railways, and telephony), product evaluation and penetration testing (OS internals and networking, reverse engineering, applied cryptography), malware analysis and incident response. He holds an MSc in Computer Science from the Technion, Israel Institute of Technology, with a focus on privacy enhancing technologies.