From 82af1d0ac01d09aa40c887b460d44b9d9f4bccd9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Br=C3=A9nainn=20Woodsend?= <bwoodsend@gmail.com>
Date: Sun, 3 May 2026 12:22:48 +0100
Subject: [PATCH] Fix failure cleanup paths in ujson.dump()

* Add missing dec-refs for if PyTuple_Pack() or writing the payload to
  file fails

* Add missing bailout for failed PyTuple_Pack()

* Add tests for all but the PyTuple_Pack() failing (which requires
  inducing a malloc() failure)
---

From 82af1d0ac01d09aa40c887b460d44b9d9f4bccd9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Br=C3=A9nainn=20Woodsend?= <bwoodsend@gmail.com>
Date: Sun, 3 May 2026 12:22:48 +0100
Subject: [PATCH] Fix failure cleanup paths in ujson.dump()

* Add missing dec-refs for if PyTuple_Pack() or writing the payload to
  file fails

* Add missing bailout for failed PyTuple_Pack()

* Add tests for all but the PyTuple_Pack() failing (which requires
  inducing a malloc() failure)
---
 python/objToJSON.c  |  7 +++++++
 tests/test_ujson.py | 33 +++++++++++++++++++++++++++++++++
 2 files changed, 40 insertions(+)

Index: ujson-5.10.0/python/objToJSON.c
===================================================================
--- ujson-5.10.0.orig/python/objToJSON.c	2024-05-14 04:00:06.000000000 +0200
+++ ujson-5.10.0/python/objToJSON.c	2026-07-28 01:08:40.540174408 +0200
@@ -895,6 +895,11 @@
   }
 
   argtuple = PyTuple_Pack(1, data);
+  if (argtuple == NULL)
+  {
+    Py_XDECREF(write);
+    return NULL;
+  }
 
   string = objToJSON (self, argtuple, kwargs);
 
@@ -911,6 +916,7 @@
   if (argtuple == NULL)
   {
     Py_XDECREF(write);
+    Py_DECREF(string);
     return NULL;
   }
 
@@ -918,6 +924,7 @@
   if (write_result == NULL)
   {
     Py_XDECREF(write);
+    Py_DECREF(string);
     Py_XDECREF(argtuple);
     return NULL;
   }
Index: ujson-5.10.0/tests/test_ujson.py
===================================================================
--- ujson-5.10.0.orig/tests/test_ujson.py	2024-05-14 04:00:06.000000000 +0200
+++ ujson-5.10.0/tests/test_ujson.py	2026-07-28 01:08:40.541617557 +0200
@@ -8,6 +8,7 @@
 import re
 import subprocess
 import sys
+import types
 import uuid
 from collections import OrderedDict
 from pathlib import Path
@@ -365,6 +366,38 @@
 def test_dump_file_args_error():
     with pytest.raises(TypeError):
         ujson.dump([], "")
+    with pytest.raises(TypeError):
+        ujson.dump([], "", "")
+
+
+def test_dump_non_callable_write():
+    file = types.SimpleNamespace(write="a")
+    with pytest.raises(TypeError):
+        ujson.dump([7] * 100, file)
+
+
+def test_failed_dump():
+    with pytest.raises(TypeError):
+        ujson.dump([[0] * 100, object()], io.StringIO())
+
+
+def test_failed_dump_bogus_file():
+    file = types.SimpleNamespace(write=lambda: None)
+    with pytest.raises(TypeError, match="0 positional arguments"):
+        ujson.dump([0] * 100, file)
+
+
+def test_failed_dump_failed_write():
+    file = types.SimpleNamespace(write=lambda x: 1 / 0)
+    with pytest.raises(ZeroDivisionError):
+        ujson.dump([0] * 100, file)
+
+
+def test_failed_dump_closed_file():
+    file = io.StringIO()
+    file.close()
+    with pytest.raises(ValueError, match="closed file"):
+        ujson.dump([0] * 100, file)
 
 
 def test_load_file():
