Skip to content

SYSTEM Cited by 1 source

Figma RenderServer

RenderServer is a server version of the Figma editor — a C++ application that performs Figma-file rendering in backend services rather than a browser. Used internally for features like thumbnailing, image export, and SVG export. Leverages GPU acceleration for rendering on the full-featured path; a non-GPU variant exists for paths that trade features for tighter sandboxing. (Source: sources/2026-04-21-figma-server-side-sandboxing-containers-and-seccomp)

Why it needs sandboxing

RenderServer processes user-supplied Figma files — potentially adversarial input delivered by anyone who can upload a design. "RenderServer has a complex set of features, leverages Graphics Processing Units (GPU) acceleration to perform rendering, and is used in multiple backend services." Complex C++ codebase + third-party image / font / rendering libraries (many memory-unsafe) + adversarial input = a canonical workload-isolation target.

The two sandboxing postures

Figma runs RenderServer under two different sandboxes, chosen per use case:

1. Full GPU path — nsjail

For rendering that requires GPU acceleration:

  • New user / pid / mount / network namespaces per request.
  • No network access.
  • Specific mount points only: input file, libraries, output folder.
  • seccomp-bpf with a strict syscall allowlist.

Chosen over Docker as a drop-in — no service rearchitecture, no orchestration tax. See systems/nsjail#Use case Figma RenderServer.

2. Non-GPU path — seccomp-only, post-refactor

For RenderServer use cases that don't need GPU acceleration, Figma stripped the sandbox down to seccomp-only — cheaper, faster, simpler to operate. The problem blocking this move was seccomp's pointer-dereference limitation: seccomp cannot filter openat by path (paths are pointer arguments), so either all openat calls are allowed or none are. RenderServer needed dynamic file access during image processing, so blocking openat would break it.

The refactor: reorder all file opens to occur before any image processing on potentially dangerous user input, then apply a restrictive seccomp filter via libseccomp that denies openat for the rest of the process lifetime.

Trade-offs disclosed:

  • Easier to test and debug than nsjail.
  • Runs significantly faster than the nsjail version.
  • ❌ Locks RenderServer into a single-threaded model.
  • ❌ Cannot dynamically load fonts or images later in runtime — all must be opened pre-lockdown.

"This new version of RenderServer is far easier to test and debug, and it runs significantly faster than the previous iteration using nsjail. But, it also introduces constraints on engineers who wish to add new features or improvements to RenderServer."

Worked example: SVG export

"Suppose we want to use RenderServer to help users export their Figma file as an SVG. In this scenario, the risky step that must be sandboxed is the processing of the Figma file, which might contain malicious input planted by an attacker who wants to hack Figma. A combination of RenderServer and various third-party libraries written in memory-unsafe languages, mostly in C++, handle processing. RenderServer will first finish generating the preview image before writing it to an output file."

The risk: a compromised RenderServer process during image processing could use openat to read arbitrary files on the system. The naive mitigation (block openat via seccomp) breaks RenderServer's own output-write step. The refactor resolves this by opening the output file first, then installing the seccomp filter, then processing the user input.

Rollout surprises

  • Default rlimit_fsize = 1 MB in nsjail silently truncated outputs for large-image inputs. Debuggable because "many errors were correlated with input files that contained large images and resulted in output files that were exactly 1 MB in size. Very suspicious!" One- line config fix.
  • Seccomp allowlist needed several rounds of expansion as production hit rare codepaths not exercised in testing. "Kernel logs will indicate when a process is killed by seccomp and which syscall caused the problem, without providing much more context" — each iteration was a mini-investigation.

Seen in

Last updated · 200 distilled / 1,178 read