浏览代码

fix(code-runtime-python): reject a non-integer maxLogBytes/maxValueBytes at load

The child reads these byte budgets through int(...), which silently floors a
float, so maxLogBytes: 3.5 would truncate at 3 bytes child-side while the host
meters and marks at 3.5 — the two sides enforcing different public config. Gate
them to integers at load, as the worker backend does; correct the stale comment
that claimed the int()-truncated caps needed no gate. Adds a regression test.
Chinesezjc 1 月之前
父节点
当前提交
bea8708b5d

+ 12 - 2
packages/code-runtime/code-runtime-python/src/index.ts

@@ -450,8 +450,10 @@ export class PythonCodeRuntime extends CodeRuntime {
     }
     // cpuSeconds crosses to the child's setrlimit(RLIMIT_CPU) raw; a float
     // raises TypeError inside every child (a late per-run failure). Reject it
-    // at load. Other numeric caps are consumed as numbers host-side or
-    // int()-truncated in the bootstrap, so they need no integer gate.
+    // at load. maxLogBytes/maxValueBytes get their own integer gate below (the
+    // child int()-truncates them, so a float would diverge from the host);
+    // maxWallMs/graceMs/addressSpaceMb are consumed as numbers where a fraction
+    // is harmless.
     if (!Number.isInteger(this.config.cpuSeconds)) {
       throw new Error(`dsh-code-runtime-python: config.cpuSeconds must be a positive integer, got ${String(this.config.cpuSeconds)}`)
     }
@@ -507,6 +509,14 @@ export class PythonCodeRuntime extends CodeRuntime {
     // is already inside the charge and must not be multiplied in again. The
     // admissible cap is therefore `ceiling - envelope`.
     for (const key of ['maxLogBytes', 'maxValueBytes'] as const) {
+      // Require an integer: the child reads these budgets through `int(...)`,
+      // which silently floors a float, so `maxLogBytes: 3.5` would truncate at 3
+      // bytes child-side while the host meters and marks at 3.5 — the two sides
+      // enforcing different public config. Reject the float at load, as the
+      // worker backend does for its byte budgets.
+      if (!Number.isInteger(this.config[key])) {
+        throw new Error(`dsh-code-runtime-python: config.${key} must be a positive integer (the child reads it as an int, so a float diverges from the host), got ${String(this.config[key])}`)
+      }
       const limit = FRAME_CEILING_BYTES - FRAME_ENVELOPE_BYTES
       if (this.config[key] > limit) {
         throw new Error(`dsh-code-runtime-python: config.${key} must not exceed ${limit} (a payload that large cannot cross the ${FRAME_CEILING_BYTES}-byte fd-3 frame ceiling, so the run would fail as worker-exit rather than output-limit), got ${String(this.config[key])}`)

+ 12 - 0
packages/code-runtime/code-runtime-python/tests/runtime.spec.ts

@@ -68,6 +68,18 @@ describe('PythonCodeRuntime — seam descriptors and misuse', () => {
       .rejects.toThrow(/cpuSeconds must be a positive integer, got 1.5/)
   })
 
+  it('rejects a non-integer byte budget at load (the child int()-truncates it)', async () => {
+    // maxLogBytes/maxValueBytes cross to the child, which reads them through
+    // int(...): a float would floor there while the host meters the fraction, so
+    // the two sides would enforce different public config. Reject at load.
+    const ctxLog = new Context()
+    await expect(ctxLog.plugin(PythonCodeRuntime, { maxLogBytes: 3.5 }))
+      .rejects.toThrow(/maxLogBytes must be a positive integer/)
+    const ctxValue = new Context()
+    await expect(ctxValue.plugin(PythonCodeRuntime, { maxValueBytes: 1024.5 }))
+      .rejects.toThrow(/maxValueBytes must be a positive integer/)
+  })
+
   it('rejects finite numeric config that cannot cross as an exact rlimit integer', async () => {
     // `Number.isFinite` and `Number.isInteger` both admit values that cannot
     // round-trip. `addressSpaceMb: 1e308` overflows to `Infinity` once multiplied