Skip to content

fix: latent Windows scs_int truncation + OOM crash in SCS_solve#200

Merged
bodono merged 1 commit into
masterfrom
fix/windows-format-and-oom
Apr 22, 2026
Merged

fix: latent Windows scs_int truncation + OOM crash in SCS_solve#200
bodono merged 1 commit into
masterfrom
fix/windows-format-and-oom

Conversation

@bodono

@bodono bodono commented Apr 22, 2026

Copy link
Copy Markdown
Owner

Summary

Two defensive fixes in SCS_solve's result-building path in scs/scsobject.h:

1. Py_BuildValue format mismatch for scs_int under DLONG

The info_dict format string used s:l (long) for scs_int fields, but scs_int is typedef'd to long long when DLONG is defined (see scs_source/include/scs_types.h), and meson.build sets -DDLONG=1 on every platform including Windows.

  • LP64 (Linux / macOS): long == long long == 64 bits, so this is benign today.
  • LLP64 (Windows): long is 32 bits while long long is 64 bits. Any scs_int value that exceeds 32 bits (iter counts, status codes, etc.) would silently truncate once we build wheels on Windows.

Switched to s:L to match scs_int's actual size. This mirrors argparse_string in SCS_init, which already used 'L' with the same rationale — added a comment cross-referencing it.

2. OOM crash + leak in numpy array construction

After copying sol->{x, y, s} into heap buffers (_x, _y, _s), we passed each to PyArray_SimpleNewFromData and unconditionally called PyArray_ENABLEFLAGS on the return. If the numpy call fails (OOM), it sets a Python exception but does not take ownership of the buffer — so:

  • PyArray_ENABLEFLAGS would dereference NULL → crash.
  • All three raw scs_malloc'd buffers would leak.
  • If the first call succeeded but the second failed, ownership would be inconsistent (first buffer owned by the array, second and third still raw).

Now each return is NULL-checked: on failure we scs_free the still-un-owned raw buffer, Py_DECREF any already-constructed arrays (which own their buffers via NPY_ARRAY_OWNDATA), and propagate NULL.

Test plan

  • Full test suite passes locally (327 passed, 66 skipped)
  • CI passes on Linux / macOS / Windows across the wheel matrix

🤖 Generated with Claude Code

Two defensive fixes in SCS_solve's result-building path:

1. Py_BuildValue format mismatch for scs_int under DLONG. The info_dict
   format string used 's:l' (long) for scs_int fields, but scs_int is
   typedef'd to 'long long' when DLONG is defined (see scs_types.h), and
   meson.build sets -DDLONG=1 on all platforms including Windows. On LP64
   (Linux/macOS) long == long long, so this is benign; on LLP64 (Windows)
   long is 32 bits and would truncate iter counts / status codes. Switch
   to 's:L' to match scs_int's actual size — mirrors argparse_string in
   SCS_init, which already used 'L' with the same rationale.

2. OOM crash + leak in numpy array construction. After copying sol->{x,y,s}
   into heap buffers, we passed each to PyArray_SimpleNewFromData and
   unconditionally called PyArray_ENABLEFLAGS on the return. If the numpy
   call fails (OOM), it sets a Python exception but does not take
   ownership of the buffer — so ENABLEFLAGS would dereference NULL and
   crash, plus all three raw buffers would leak. Check each return,
   free the un-owned raw buffer, Py_DECREF any already-constructed arrays
   (which own their buffers via NPY_ARRAY_OWNDATA), and propagate NULL.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@bodono
bodono merged commit ec27459 into master Apr 22, 2026
34 checks passed
@bodono
bodono deleted the fix/windows-format-and-oom branch April 22, 2026 20:25
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.

1 participant