Skip to content

Keep existing robots working with the robot security (#124) - #126

Open
flemming-n-larsen wants to merge 9 commits into
mainfrom
fix-issue-124
Open

flemming-n-larsen wants to merge 9 commits into
mainfrom
fix-issue-124

Conversation

@flemming-n-larsen

@flemming-n-larsen flemming-n-larsen commented Nov 3, 2026 •

Copy link
Copy Markdown
Member

Fixes #124. Since 1.11.2, robots are rejected when loaded if they refer to something they may not use, even in code that never runs. Many of these robots are no longer updated, so this makes them work without any changes, inside Robocode.

  • Allowed: Logger, Level, AWTKeyStroke, MessageDigest and SecureRandom, except the methods that change global state or load resource bundles.
  • Replaced when loaded: ImageIO.write to a stream, new Scanner(File) with the same check as FileReader, and Thread.stop() on a robot's own threads. Java 20+ can't stop threads, so there it interrupts.
  • Stopped when run, as with the SecurityManager: FileOutputStream, FileWriter and FileReader(FileDescriptor). Nothing is opened, and subclasses of FileOutputStream and FileWriter are still rejected when loaded.

FileOutputStream, FileWriter and ZipFile are replaced with small subclasses in robocode.util, so new stays valid. Scanner is final, so its constructor call is replaced with a static call. That is rejected with a clear message if the arguments contain a branch, or when it is used as a method reference.

Tested: all tests on Java 17, 25 and 26 (141). 23 RoboRumble robots ran against sample.Fire on Java 8 and 26, none stopped by the sandbox. KurtBot and Omni 0.06 are built for Java 17 and ran on Java 26 only. WhiteFang throws an exception from its own code at random, on all Java versions.

Please check these in particular:

  • Robot threads have the robot's class loader as context class loader when ImageIO looks up its plugins. A plugin in a robot jar should be rejected by the verifier, as javax/imageio/spi isn't allowed.
  • The doPrivileged calls in RobotCompat.
  • Is the list of denied Logger methods complete?

@Chase-san, could you try this against AutoRumble? @jkflying, I'd like your eyes on the sandbox parts.

…n they are loaded

Scanner on a file, ImageIO.write, Thread.stop on a robot's own threads, and creating a FileOutputStream, FileWriter
or FileReader of a file descriptor no longer prevent a robot from being loaded. As with the SecurityManager, a robot
is stopped when it really tries to do what it is not allowed to do.
@Chase-san

Copy link
Copy Markdown
Contributor

It's funny I was just starting to make a PR when you posted this. So I will read over these.

@Chase-san

Copy link
Copy Markdown
Contributor

Every robot on the roborumble 1-vs-1 list was tested.

e32.Omni 0.06 is rejected by both builds but wasn't yet found. Its bundled commons-math3 RandomDataGenerator uses MessageDigest and SecureRandom, which aren’t allowed. It runs normally on 1.11.1, but it broke with 1.11.2. Both classes look safe to allow.

  • These 15 now load and run, with no security violations: Garm, Gir, ScruchiPu, TheBrainPi, KurtBot, Raven, WhiteFang, ColdBreath, Toad, Leonidas, Wavelet, AgentSmith, Knight, LionWWSVMvoid, NeuralPremier. Nothing that worked on the base is rejected or stopped with the PR.
  • Still rejected, as expected: Lambda 0.017, catcat20.shape.Theta 0.018 (same gson), MorbidPriest, UnderDog.

Melee, twinduel, and teams are pending.

@Chase-san

Chase-san commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Other robots blocked

  • kawigi.sbf.TidalWave 0.8 (teams): its member FloodHT 0.8 opens its own data file with new ZipFile(File), which the old security manager allowed.
  • inonit.robocode.javascript.MyFirstRobot 1.1 (melee): it bundles a whole scripting engine with its own class loaders, URL connections, javax.tools and System.exit. It should probably stay blocked.
  • amk.ChumbaMini and kawigi.sbf.FloodSonnet hit the 5-open-streams limit.
  • catcat20.Rabbit 0.001 only looked blocked: its jar also contains the rejected Lambda 0.011.

Other issues worth noting:
On Java 24+, HostManager replaces System.in with SecureInputStream. Its access check always throws there, so nothing in the JVM can read stdin, not even the host program. My harness hit this, and the fix was to open stdin before starting the engine.

…own files, and load the time zone rules

Robots were punished for Java looking up the providers of the time zone rules on the class path with the
SecurityManager, the first time they used e.g. ZipOutputStream.
# Conflicts:
#	robocode.tests/src/test/java/net/sf/robocode/test/host/security/RobotClassVerifierTest.java
… handle more ways of using the replaced calls

Replacing the new instruction with a method call only works without branches in the arguments, as the stack map refers
to it, so it is now only used for Scanner, which is final, and is rejected with a clear message when it cannot be used.
Also support Thread.stop() via a subclass, method handles, the constructors with a Charset, and stopping a thread before
it starts. ImageIO no longer looks up plugins with the class loader of the robot, and Logger cannot be extended to load
a resource bundle.
@flemming-n-larsen

Copy link
Copy Markdown
Member Author

Thanks, this was very thorough. I've pushed updates to the PR, and everything you listed is covered except where noted:

  • e32.Omni 0.06: MessageDigest and SecureRandom are allowed now. It runs on Java 26. It's built for Java 17, so it can't run on Java 8.
  • kawigi.sbf.TidalWave 0.8 (FloodHT): new ZipFile(File) works on a robot's own files. The team runs fine on Java 8 and 26.
  • System.in on Java 24+: Fixed on main. The program using Robocode can read it, and robots get a replacement that stops them if they read from it.
  • amk.ChumbaMini / kawigi.sbf.FloodSonnet: These don't leave anything open on Robocode's side. ChumbaMini doesn't close its files and hits the 5-stream limit in its own code, with or without this PR. FloodSonnet ran fine here. The limit is from 2005, so I haven't changed it.
  • Still blocked, as you suggested: MyFirstRobot (script engine), Lambda/Theta (gson), MorbidPriest, UnderDog.

The review also led to more fixes: Thread.stop() via a Thread subclass and method references, the Charset constructors of FileWriter and Scanner, and a safer ImageIO.

I ran 23 of these robots against sample.Fire on Java 8 and 26, and none are stopped by the sandbox. Could you rerun your harness, especially melee, twin duel and teams? And does anything else on your lists still fail?

@Chase-san

Copy link
Copy Markdown
Contributor

I'll rerun the entire set, it might take an hour or two however.

@Chase-san

Copy link
Copy Markdown
Contributor

Retested with all four rumbles (1,247 1v1 robots, 430 melee, 51 teams, 28 twin-duel teams), on Java 21 with the SecurityManager and on Java 25. No regressions, no robot stopped for a security violation, and no failed battles. e32.Omni 0.06 and kawigi.sbf.TidalWave 0.8 now load and fight.

One thing that isn’t from this PR, but that the PR makes visible: Krabb.sliNk.Garm catches a StackOverflowError in its movement code and then gets the stack trace. If that’s the first stack trace through java.base in the JVM, StackTraceElement$HashedModules fails to initialize on the nearly full stack, and from then on every getStackTrace() in the JVM throws NoClassDefFoundError. Before this PR, the stack trace Robocode printed for TidalWave’s rejection at startup initialized that class first. Taking one stack trace through JDK code at startup, e.g. from a caught Integer.parseInt("x"), prevents it: GarmTeam vs ImWithStupidTeam went from 4 of 8 failing to 0 of 8 on Java 25.

@flemming-n-larsen

Copy link
Copy Markdown
Member Author

Thanks for finding the stack trace problem. The fix is on main: Robocode now takes a stack trace through JDK code when it starts. I measured it on Java 25 with this branch, using the real Krabb.sliNk.GarmTeam 0.9v against sampleteam.MyFirstTeam (I couldn't get ImWithStupid). Without the fix, stack traces were broken in the JVM after 11 of 12 battles, and with it in none of 12. Garm still prints its own "Nullpointer exception in getWay!" messages when it overflows the stack itself, but that no longer affects other robots.

@Chase-san

Copy link
Copy Markdown
Contributor

No need to wait on me, probably won't have time to poke at the autorumble more until this weekend.

# Conflicts:
#	robocode.host/src/main/java/net/sf/robocode/host/HostManager.java
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rumble robots blocked

2 participants