mirror of
https://github.com/compiler-explorer/compiler-explorer.git
synced 2025-12-27 10:33:59 -05:00
Limit exec streams to max string length (#5898)
Some operations set `exec`'s `maxOutput` to a value larger than the max string length. This change ensures we always cap output to the engine's string limit (currently 512 MB in recent Node versions). This also tweaks handling when reaching the string limit to ensure adding the "truncated" message itself does not send us beyond the limit.
This commit is contained in:
@@ -1428,7 +1428,15 @@ export class BaseCompiler implements ICompiler {
|
||||
|
||||
if (output.timedOut) {
|
||||
return {
|
||||
error: 'Clang invocation timed out',
|
||||
error: 'Invocation timed out',
|
||||
results: {},
|
||||
compileTime: output.execTime || compileEnd - compileStart,
|
||||
};
|
||||
}
|
||||
|
||||
if (output.truncated) {
|
||||
return {
|
||||
error: 'Exceeded max output limit',
|
||||
results: {},
|
||||
compileTime: output.execTime || compileEnd - compileStart,
|
||||
};
|
||||
|
||||
@@ -203,7 +203,15 @@ export class RacketCompiler extends BaseCompiler {
|
||||
|
||||
if (output.timedOut) {
|
||||
return {
|
||||
error: 'Racket invocation timed out',
|
||||
error: 'Invocation timed out',
|
||||
results: {},
|
||||
compileTime: output.execTime || compileEnd - compileStart,
|
||||
};
|
||||
}
|
||||
|
||||
if (output.truncated) {
|
||||
return {
|
||||
error: 'Exceeded max output limit',
|
||||
results: {},
|
||||
compileTime: output.execTime || compileEnd - compileStart,
|
||||
};
|
||||
|
||||
15
lib/exec.ts
15
lib/exec.ts
@@ -24,6 +24,7 @@
|
||||
|
||||
import child_process from 'child_process';
|
||||
import path from 'path';
|
||||
import buffer from 'buffer';
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import treeKill from 'tree-kill';
|
||||
@@ -59,7 +60,7 @@ export function executeDirect(
|
||||
filenameTransform?: FilenameTransformFunc,
|
||||
): Promise<UnprocessedExecResult> {
|
||||
options = options || {};
|
||||
const maxOutput = options.maxOutput || 1024 * 1024;
|
||||
const maxOutput = Math.min(options.maxOutput || 1024 * 1024, buffer.constants.MAX_STRING_LENGTH);
|
||||
const timeoutMs = options.timeoutMs || 0;
|
||||
const env = {...process.env, ...options.env};
|
||||
|
||||
@@ -123,8 +124,10 @@ export function executeDirect(
|
||||
if (streams.truncated) return;
|
||||
const newLength = streams[name].length + data.length;
|
||||
if (maxOutput > 0 && newLength > maxOutput) {
|
||||
streams[name] = streams[name] + data.slice(0, maxOutput - streams[name].length);
|
||||
streams[name] += '\n[Truncated]';
|
||||
const truncatedMsg = '\n[Truncated]';
|
||||
const spaceLeft = Math.max(maxOutput - streams[name].length - truncatedMsg.length, 0);
|
||||
streams[name] = streams[name] + data.slice(0, spaceLeft);
|
||||
streams[name] += truncatedMsg.slice(0, maxOutput - streams[name].length);
|
||||
streams.truncated = true;
|
||||
kill();
|
||||
return;
|
||||
@@ -162,7 +165,11 @@ export function executeDirect(
|
||||
truncated: streams.truncated,
|
||||
execTime: ((endTime - startTime) / BigInt(1000000)).toString(),
|
||||
};
|
||||
logger.debug('Execution', {type: 'executed', command: command, args: args, result: result});
|
||||
// Check debug level explicitly as result may be a very large string
|
||||
// which we'd prefer to avoid preparing if it won't be used
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug('Execution', {type: 'executed', command: command, args: args, result: result});
|
||||
}
|
||||
resolve(result);
|
||||
});
|
||||
if (child.stdin) {
|
||||
|
||||
@@ -118,7 +118,7 @@ describe('Execution tests', () => {
|
||||
});
|
||||
it('limits output', () => {
|
||||
return exec
|
||||
.execute('echo', ['A very very very very very long string'], {maxOutput: 10})
|
||||
.execute('echo', ['A very very very very very long string'], {maxOutput: 22})
|
||||
.then(testExecOutput)
|
||||
.should.eventually.deep.equals({
|
||||
code: 0,
|
||||
|
||||
@@ -146,6 +146,7 @@ export type CompilationResult = {
|
||||
devices?: Record<string, CompilationResult>;
|
||||
stdout: ResultLine[];
|
||||
stderr: ResultLine[];
|
||||
truncated?: boolean;
|
||||
didExecute?: boolean;
|
||||
execResult?: {
|
||||
stdout?: ResultLine[];
|
||||
|
||||
@@ -29,6 +29,8 @@ export type BasicExecutionResult = {
|
||||
execTime: string;
|
||||
processExecutionResultTime?: number;
|
||||
timedOut: boolean;
|
||||
languageId?: string;
|
||||
truncated?: boolean;
|
||||
};
|
||||
|
||||
export enum RuntimeToolType {
|
||||
|
||||
Reference in New Issue
Block a user