Denial of Service via Invalid Argument in h2oai/h2o-3
Description
In h2oai/h2o-3 version 3.46.0, the run_tool command in the rapids component allows the main function of any class under the water.tools namespace to be called. One such class, MojoConvertTool, crashes the server when invoked with an invalid argument, causing a denial of service.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
CVE-2024-5979 allows denial of service via the MojoConvertTool class in H2O-3 3.46.0, crashing the server with an invalid argument.
Vulnerability
Description CVE-2024-5979 affects H2O-3 version 3.46.0, specifically the run_tool command in the rapids component. This command permits calling the main function of any class under the water.tools namespace. One such class, MojoConvertTool, when invoked with an invalid argument, crashes the server, causing a denial of service [2]. The root cause is that the tool's main method could call System.exit or otherwise terminate the JVM without proper error handling [3].
Exploitation
An attacker must be able to execute the run_tool command, which is accessible within the H2O-3 environment. No authentication is required beyond access to the Rapids API or command line interface. By crafting a request with an invalid argument (e.g., a malformed file path), the attacker triggers the crash, leading to a denial of service. The vulnerability requires no special privileges and can be exploited remotely if the H2O-3 service is exposed [1][2].
Impact
Successful exploitation results in a denial of service, as the server process terminates. This disrupts any ongoing machine learning operations, model serving, or data processing tasks, potentially impacting availability for all users of the affected H2O-3 instance.
Mitigation
The issue is fixed in commit d0899f8e0f7a584b60405a65b1d7b439aaaa55a5, which removes System.exit calls from water.tools classes and adds internal error handling [3]. Users are strongly advised to update H2O-3 to a version containing this patch. No workarounds have been provided by the vendor other than applying the update. The vulnerability has not been listed in CISA's Known Exploited Vulnerabilities (KEV) catalog as of the publication date [2].
- GitHub - h2oai/h2o-3: H2O is an Open Source, Distributed, Fast & Scalable Machine Learning Platform: Deep Learning, Gradient Boosting (GBM) & XGBoost, Random Forest, Generalized Linear Modeling (GLM with Elastic Net), K-Means, PCA, Generalized Additive Models (GAM), RuleFit, Support Vector Machine (SVM), Stacked Ensembles, Automatic Machine Learning (AutoML), etc.
- NVD - CVE-2024-5979
- [GH-16351] Do not call System.exit from water.tools [nocheck] (#16366) · h2oai/h2o-3@d0899f8
AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
h2oPyPI | <= 3.46.0 | — |
Affected products
2- h2oai/h2oai/h2o-3v5Range: unspecified
Patches
1d0899f8e0f7a[GH-16351] Do not call System.exit from water.tools [nocheck] (#16366)
4 files changed · +33 −19
h2o-algos/src/main/java/water/tools/MojoConvertTool.java+14 −11 modified@@ -33,25 +33,28 @@ void convert() throws IOException { Files.write(pojoPath, pojo.getBytes(StandardCharsets.UTF_8)); } - private static void usage() { - System.err.println("java -cp h2o.jar " + MojoConvertTool.class.getName() + " source_mojo.zip target_pojo.java"); - } - public static void main(String[] args) throws IOException { - if (args.length < 2) { - usage(); + try { + mainInternal(args); + } + catch (IllegalArgumentException e) { + System.err.println(e.getMessage()); System.exit(1); } + } + + public static void mainInternal(String[] args) throws IOException { + if (args.length < 2 || args[0] == null || args[1] == null) { + throw new IllegalArgumentException("java -cp h2o.jar " + MojoConvertTool.class.getName() + " source_mojo.zip target_pojo.java"); + } File mojoFile = new File(args[0]); - if (!mojoFile.isFile()) { - System.err.println("Specified MOJO file (" + mojoFile.getAbsolutePath() + ") doesn't exist!"); - System.exit(2); + if (!mojoFile.exists() || !mojoFile.isFile()) { + throw new IllegalArgumentException("Specified MOJO file (" + mojoFile.getAbsolutePath() + ") doesn't exist!"); } File pojoFile = new File(args[1]); if (pojoFile.isDirectory() || (pojoFile.getParentFile() != null && !pojoFile.getParentFile().isDirectory())) { - System.err.println("Invalid target POJO file (" + pojoFile.getAbsolutePath() + ")! Please specify a file in an existing directory."); - System.exit(3); + throw new IllegalArgumentException("Invalid target POJO file (" + pojoFile.getAbsolutePath() + ")! Please specify a file in an existing directory."); } System.out.println();
h2o-core/src/main/java/water/rapids/ast/prims/internal/AstRunTool.java+4 −2 modified@@ -33,10 +33,12 @@ public ValStr apply(Env env, Env.StackHelp stk, AstRoot[] asts) { try { // only allow to run approved tools (from our package), not just anything on classpath Class<?> clazz = Class.forName(TOOLS_PACKAGE + toolClassName); - Method mainMethod = clazz.getDeclaredMethod("main", String[].class); + Method mainMethod = clazz.getDeclaredMethod("mainInternal", String[].class); mainMethod.invoke(null, new Object[]{args}); } catch (Exception e) { - throw new RuntimeException(e); + RuntimeException shorterException = new RuntimeException(e.getCause().getMessage()); + shorterException.setStackTrace(new StackTraceElement[0]); + throw shorterException; } return new ValStr("OK"); }
h2o-core/src/main/java/water/tools/EncryptionTool.java+3 −0 modified@@ -47,6 +47,9 @@ public void encrypt(File input, File output) throws IOException, GeneralSecurity } public static void main(String[] args) throws GeneralSecurityException, IOException { + mainInternal(args); + } + public static void mainInternal(String[] args) throws GeneralSecurityException, IOException { EncryptionTool et = new EncryptionTool(); et._keystore_file = new File(args[0]); et._keystore_type = args[1];
h2o-extensions/xgboost/src/main/java/water/tools/XGBoostLibExtractTool.java+12 −6 modified@@ -10,19 +10,25 @@ public class XGBoostLibExtractTool { public static void main(String[] args) throws IOException { + try { + mainInternal(args); + } catch (IllegalArgumentException e) { + System.err.println((e.getMessage())); + System.exit(1); + } + } + + public static void mainInternal(String[] args) throws IOException { if (args.length != 1) { - System.err.println("XGBoostLibExtractTool: Specify target directory where to extract XGBoost native libraries."); - System.exit(-1); + throw new IllegalArgumentException("XGBoostLibExtractTool: Specify target directory where to extract XGBoost native libraries."); } File dir = new File(args[0]); if (!dir.exists()) { - System.err.println("XGBoostLibExtractTool: Directory '" + dir.getAbsolutePath() + "' doesn't exist."); - System.exit(-1); + throw new IllegalArgumentException("XGBoostLibExtractTool: Directory '" + dir.getAbsolutePath() + "' doesn't exist."); } NativeLibraryLoaderChain loader = XGBoostExtension.getLoader(); if (loader == null) { - System.err.println("XGBoostLibExtractTool: Failed to locate native libraries."); - System.exit(-1); + throw new IllegalArgumentException("XGBoostLibExtractTool: Failed to locate native libraries."); } for (NativeLibrary lib : loader.getNativeLibs()) { if (!lib.isBundled())
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4News mentions
0No linked articles in our index yet.