---
id: RUSTSEC-2026-0236
aliases:
  - GHSA-6976-qm5m-7mcj
title: >-
  A `BigInt` division panics, and two neighbouring operations answer wrongly in
  silence
summary: >-
  A `BigInt` division panics, and two neighbouring operations answer wrongly in
  silence
severity: high
cvss: 7.5
cvssVector: 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H'
vendor: viperjs
product: viperjs
ecosystem: rust
affected:
  - 'viperjs >= 0.0.0-0, < 0.2.2'
patched:
  - viperjs 0.2.2
published: '2026-08-06'
updated: '2026-08-06'
source: OSV
sourceUrl: 'https://osv.dev/vulnerability/RUSTSEC-2026-0236'
references:
  - url: 'https://crates.io/crates/viperjs'
  - url: 'https://rustsec.org/advisories/RUSTSEC-2026-0236.html'
  - url: >-
      https://github.com/MerlijnW70/viperjs/security/advisories/GHSA-6976-qm5m-7mcj
  - url: 'https://github.com/MerlijnW70/viperjs/releases/tag/v0.2.2'
tags:
  - osv
  - rust
ingestedAt: '2026-08-06T19:13:31.977Z'
---

## Overview

`viperjs` is a JavaScript engine intended to run untrusted script inside a host application, so
script text is data rather than a trusted caller and a panic reachable from script is a denial of
service in the embedder's process.

A divisor whose magnitude lands exactly on the engine's internal limb ceiling reaches an
out-of-bounds index. On every released version up to and including 0.2.1:

```js
const d = (1n << 33554399n) * 2n;
1n / d;   // panic: index out of bounds
```

Two further operations on the same value returned wrong results without raising anything, which
is the more dangerous half for an embedder that acts on the answer:

```js
d % 7n;      // 0n  — the true remainder is 1n
String(d);   // "0"
```

## Cause

The left-shift helper reserved one limb for the bits a shift may push past the top of the
magnitude, measured *that* width against the size ceiling, and then trimmed the reserved limb away
again — so a magnitude landing exactly on the ceiling was refused on account of room it does not
keep. The division treated that refusal as unreachable and discarded it with `unwrap_or_default`,
leaving an empty divisor magnitude; the subsequent `divisor[n - 1]` is then an index of
`usize::MAX`.

The crate is `#![forbid(unsafe_code)]`, so this is a panic and not memory unsafety.

## Remediation

Upgrade to 0.2.2, in which all three behaviours are fixed: both divisions now produce correct
results, and `String()` of a magnitude beyond what the engine can divide raises a `RangeError`
rather than producing `"0"` — ECMA-262 §6.1.4 requires an implementation that imposes a limit to
throw rather than answer something else.

There is no workaround short of upgrading; the values are reachable from any script the embedder
evaluates.

Reported by [@Zniece](https://github.com/Zniece).

## Affected packages

- `viperjs >= 0.0.0-0, < 0.2.2`

## Remediation

Upgrade to a patched release:

- `viperjs 0.2.2`
