Skip to content

Conversation

@AnastaZIuk
Copy link
Member

No description provided.

OfekShilon and others added 30 commits August 9, 2025 20:59
refs compiler-explorer#7948

Signed-off-by: Marc Poulhiès <dkm@kataplop.net>
refs compiler-explorer#7948

Signed-off-by: Marc Poulhiès <dkm@kataplop.net>
…xplorer#8017)

- Made parsers stateful instances instead of shared static state (for
mllvm options). Fixes compiler-explorer#8011 as this is caused by multiple clang-based
compilers being run concurrently and stomping over each others' state.
- passes `Compiler` to the constructor, which removes some param passing
- Added some missing awaits
- Tried to get things less dependent on `examples`, only `go` needs it
- Spotted that `zig` c++ might have issues in discovery
- Fly-by fixed a broken go path in ppc64le_gl122
- removed a redundant override in coccinelle
- made the mojo parser actually use the parser it defined
- canonified tablegen's special method
- 

I changed the zig parser too but as best I can tell it was broken before
(the `1` return value from the command it runs:)

```
ubuntu@ip-172-30-0-164:/infra/.deploy$ /opt/compiler-explorer/zig-0.14.1/zig c++ -mllvm --help-list-hidden /infra/.deploy/examples/c++/default.cpp -S -o /tmp/output.s
...
  --x86-use-vzeroupper                                                       - Minimize AVX to SSE transition penalty
  --xcore-max-threads=<number>                                               - Maximum number of threads (for emulation thread-local storage)
/infra/.deploy/examples/c++/default.cpp:1:1: error: FileNotFound
```
return code 1 (means it's not cached)

---------

Co-authored-by: Partouf <partouf@gmail.com>
…er#8003)

Bump Binutils version into 2.45 when using GCC 15.1.

The change form 2.44 into 2.45 includes some new assembler features, the
details please check


https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gas/NEWS;h=2e9538bd2736b4108cc5a383802c75f5feacfbc4;hb=2bc7af1ff7732451b6a7b09462a815c3284f9613

Signed-off-by: Jiawei <jiawei@iscas.ac.cn>
…orer#8018)

Some compilers emit symbol names with multiple dots, e. g. Clang for
static data (like string literals) or GCC for function clones (e. g. due
to interprocedural constant propagation).

Test cases: https://godbolt.org/z/dGbGncnh8,
https://godbolt.org/z/dhrGGqrjo
`AsmParser.labelFindFor` was only called in tests.
This PR adds the C++ library **fusedkernellibrary** version Beta-0.1.9
to Compiler Explorer.

- GitHub URL:
https://github.com/Libraries-Openly-Fused/FusedKernelLibrary


Related PR: compiler-explorer/infra#1755

---
_PR created with
[ce-lib-wizard](https://github.com/compiler-explorer/ce-library-wizard)_

---------

Co-authored-by: Albert Andaluz <albertandaluz@hotmail.com>
Co-authored-by: Patrick Quist <partouf@gmail.com>
This _maybe_ shows an issue - users could find _anything_ in cefs so we
need to make sure we don't ever put privileged stuff in cefs.
makes the root bindmount a "slave" which means it gets all host mount
point change notifications, needed for cefs automounting. Security
tradeoffs mentioned in compiler-explorer/nsjail#2
ISPC v1.28.0 has been released. Related infra change:
compiler-explorer/infra#1762
…explorer#8038)

Trying to solve the root cause:

`Editor.maybeEmitChange` calls `this.eventHub.emit('editorChange')`, and
then `Compiler.onEditorChange` calls `this.compile()` .
So the idiom
```ts
  this.maybeEmitChange();
  this.requestCompilation();
```
repeated a few times in editor.ts, causes two rapid (and redundant)
invocations of `compile`, which in turn cause the race. This PR removes
all calls to `Editor.requestCompilation` and in fact removes the
function entirely.

I'm guessing `maybeEmitChange` is a late addition to solve some other
problems, and this race was an unintended side effect, but please
correct me if you have better understanding of the code and its history.
<!-- THIS COMMENT IS INVISIBLE IN THE FINAL PR, BUT FEEL FREE TO REMOVE
IT
Thanks for taking the time to improve CE. We really appreciate it.
Before opening the PR, please make sure that the tests & linter pass
their checks,
  by running `make check`.
In the best case scenario, you are also adding tests to back up your
changes,
  but don't sweat it if you don't. We can discuss them at a later date.
Feel free to append your name to the CONTRIBUTORS.md file
Thanks again, we really appreciate this!
-->

---------

Co-authored-by: Patrick Quist <partouf@gmail.com>
There are two clang reflection implementations:

* The one from Matúš that implements the Reflection TS
* The one from Dan that implements p2996

The latter is what will be in C++26, but that's hard to tell from the
labels since the former is labeled "reflection" while the latter is
labeled "experimental p2996". This changes the labels to "reflection -
TS" and "relfection - C++26", respectively, to make it clearer as to
which is which.
* support c++20, c++2b, c++23, c++2c
* set the default standard to cpp17 as it is currently the default for
both compiler-explorer and cppinsights
Comment on lines +132 to +139
const clojureOptions = _.compact([
'-Sdeps',
this.defaultDeps,
...classpathArgument,
...wrapperInvokeArgument,
...userOptions,
inputFilename,
]);

Check failure

Code scanning / CodeQL

Loop bound injection High

Iteration over a user-controlled object with a potentially unbounded .length property from a
user-provided value
.

Copilot Autofix

AI 7 days ago

General approach: ensure that anything we spread or iterate over is a genuine, bounded array. For user-controlled data that may be passed as an array-like object, first validate/normalize it to a real array and, optionally, cap its length to a reasonable maximum. This prevents an attacker from supplying an object with a malicious .length value.

Concrete fix here: immediately before constructing clojureOptions in ClojureCompiler.runCompiler, normalize userOptions into a safe array and bound its length. We can do this by:

  1. Coercing options to an array of strings, and
  2. Deriving userOptions from that safe array instead of the possibly tainted original.

We do not need new imports; we can implement the normalization inline. A reasonable defensive limit can be chosen (e.g., 1000 options) to avoid absurdly large inputs without affecting legitimate usage.

Specific changes in lib/compilers/clojure.ts:

  • After the code that determines sourceFileOptionIndex, compute a safeOptions array that:
    • Treats non-arrays as empty.
    • Converts all entries to strings.
    • Truncates the number of elements to a defined maximum (e.g., 1000).
  • Use safeOptions.slice(0, sourceFileOptionIndex) (or the whole safeOptions if the index is -1) to derive userOptions.
  • Leave all other logic intact.

No changes are required in lib/handlers/compile.ts, lib/base-compiler.ts, or lib/compilers/win32.ts to resolve this particular loop-bound/spread issue; the mitigation at the use site in ClojureCompiler is sufficient to break the tainted flow into an unsafe spread.


Suggested changeset 1
lib/compilers/clojure.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/lib/compilers/clojure.ts b/lib/compilers/clojure.ts
--- a/lib/compilers/clojure.ts
+++ b/lib/compilers/clojure.ts
@@ -126,7 +126,17 @@
         const sourceFileOptionIndex = options.findIndex(option => {
             return option.endsWith('.clj');
         });
-        const userOptions = options.slice(0, sourceFileOptionIndex);
+
+        // Normalize and bound user-controlled options to avoid untrusted length issues.
+        const MAX_USER_OPTIONS = 1000;
+        const safeOptions = Array.isArray(options)
+            ? options.slice(0, MAX_USER_OPTIONS).map(option => String(option))
+            : [];
+        const userOptions =
+            sourceFileOptionIndex === -1
+                ? safeOptions
+                : safeOptions.slice(0, Math.min(sourceFileOptionIndex, safeOptions.length));
+
         const classpathArgument = await this.getClojureClasspathArgument(execOptions.customCwd, compiler, execOptions);
         const wrapperInvokeArgument = ['-M', this.compilerWrapperPath];
         const clojureOptions = _.compact([
EOF
@@ -126,7 +126,17 @@
const sourceFileOptionIndex = options.findIndex(option => {
return option.endsWith('.clj');
});
const userOptions = options.slice(0, sourceFileOptionIndex);

// Normalize and bound user-controlled options to avoid untrusted length issues.
const MAX_USER_OPTIONS = 1000;
const safeOptions = Array.isArray(options)
? options.slice(0, MAX_USER_OPTIONS).map(option => String(option))
: [];
const userOptions =
sourceFileOptionIndex === -1
? safeOptions
: safeOptions.slice(0, Math.min(sourceFileOptionIndex, safeOptions.length));

const classpathArgument = await this.getClojureClasspathArgument(execOptions.customCwd, compiler, execOptions);
const wrapperInvokeArgument = ['-M', this.compilerWrapperPath];
const clojureOptions = _.compact([
Copilot is powered by AI and may make mistakes. Always verify output.
return option.endsWith('.clj');
});
const userOptions = options.slice(0, sourceFileOptionIndex);
const clojureOptions = _.compact([...userOptions, '--macro-expand', inputFilename]);

Check failure

Code scanning / CodeQL

Loop bound injection High

Iteration over a user-controlled object with a potentially unbounded .length property from a
user-provided value
.

Copilot Autofix

AI 7 days ago

In general, to fix loop bound injection problems on arrays coming from untrusted input, you either (a) ensure the value is a genuine array with a reasonable maximum length, or (b) avoid directly iterating/spreading over something that may not be an array (e.g., coerce it into a safe representation first).

For this specific case, the minimal, behavior-preserving fix is to normalize userOptions in generateClojureMacroExpansion to a safe array before the spread, and to ensure its length is bounded. We can do this by:

  • Ensuring userOptions is an actual array: if sourceFileOptionIndex is -1 (source file not found), we fall back to an empty array rather than options.slice(0, -1) and we explicitly cast/normalize slices to arrays.
  • Clamping the maximum number of options we will process (e.g., 1,000 elements), so that an attacker cannot force our code to iterate or spread across an array with an enormous .length.
  • This can be implemented entirely within lib/compilers/clojure.ts, near the existing code, without changing external behavior for normal, small inputs.

Concretely:

  • In generateClojureMacroExpansion, after computing sourceFileOptionIndex, replace the direct options.slice(0, sourceFileOptionIndex) with logic that:
    • Treats sourceFileOptionIndex < 0 or !Array.isArray(options) as “no user options”.
    • Limits the slice to a maximum length (e.g., MAX_USER_OPTIONS = 1000).
  • Use this sanitized userOptions when building clojureOptions.

No other files (compile.ts, base-compiler.ts, win32.ts) need changes for this particular alert; the taint chain there is already constrained by other internal processing, and the direct unbounded spread is only in ClojureCompiler.generateClojureMacroExpansion.


Suggested changeset 1
lib/compilers/clojure.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/lib/compilers/clojure.ts b/lib/compilers/clojure.ts
--- a/lib/compilers/clojure.ts
+++ b/lib/compilers/clojure.ts
@@ -150,7 +150,14 @@
         const sourceFileOptionIndex = options.findIndex(option => {
             return option.endsWith('.clj');
         });
-        const userOptions = options.slice(0, sourceFileOptionIndex);
+        // Ensure we only work with a real array of reasonable size to avoid
+        // potential DoS from a maliciously large or spoofed "options" value.
+        const MAX_USER_OPTIONS = 1000;
+        let userOptions: string[] = [];
+        if (Array.isArray(options) && sourceFileOptionIndex > 0) {
+            const safeEnd = Math.min(sourceFileOptionIndex, MAX_USER_OPTIONS);
+            userOptions = options.slice(0, safeEnd);
+        }
         const clojureOptions = _.compact([...userOptions, '--macro-expand', inputFilename]);
         const output = await this.runCompiler(
             this.compiler.exe,
EOF
@@ -150,7 +150,14 @@
const sourceFileOptionIndex = options.findIndex(option => {
return option.endsWith('.clj');
});
const userOptions = options.slice(0, sourceFileOptionIndex);
// Ensure we only work with a real array of reasonable size to avoid
// potential DoS from a maliciously large or spoofed "options" value.
const MAX_USER_OPTIONS = 1000;
let userOptions: string[] = [];
if (Array.isArray(options) && sourceFileOptionIndex > 0) {
const safeEnd = Math.min(sourceFileOptionIndex, MAX_USER_OPTIONS);
userOptions = options.slice(0, safeEnd);
}
const clojureOptions = _.compact([...userOptions, '--macro-expand', inputFilename]);
const output = await this.runCompiler(
this.compiler.exe,
Copilot is powered by AI and may make mistakes. Always verify output.

Check failure

Code scanning / CodeQL

Loop bound injection High

Iteration over a user-controlled object with a potentially unbounded .length property from a
user-provided value
.

Copilot Autofix

AI 7 days ago

In general, to fix this class of issue you must ensure that any value whose .length ends up driving iteration is either: (a) guaranteed by type and construction to be a safe array, or (b) validated and normalized before use. Here, result.compilationOptions comes from tainted input and is later used with _.intersection, which treats it as array‑like. The safest fix is to defensively normalize options inside NimCompiler.postProcess so that it is always a bounded string[] regardless of what upstream provided.

Concretely, in lib/compilers/nim.ts within postProcess, we will:

  1. Replace the direct assignment const options = result.compilationOptions; with a small normalization step:
    • If result.compilationOptions is already an array, use it.
    • If it is a string, wrap it as a single‑element array.
    • Otherwise, fall back to an empty array.
  2. Optionally, enforce a reasonable maximum length for safety (e.g., clamp to some high upper bound), but to avoid altering expected behavior we will only normalize type, not truncate.
  3. Use this normalized local options in the calls to _.intersection and getCacheFile, eliminating the use of the non‑null assertion options!.

This keeps existing functionality intact for valid inputs (arrays of options) while preventing a malicious request from supplying a non‑array with a malicious .length value.

Only lib/compilers/nim.ts needs changes.

Suggested changeset 1
lib/compilers/nim.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/lib/compilers/nim.ts b/lib/compilers/nim.ts
--- a/lib/compilers/nim.ts
+++ b/lib/compilers/nim.ts
@@ -97,13 +97,18 @@
         outputFilename: string,
         filters: ParseFiltersAndOutputOptions,
     ) {
-        const options = result.compilationOptions;
+        const rawOptions = result.compilationOptions;
+        const options: string[] = Array.isArray(rawOptions)
+            ? rawOptions
+            : typeof rawOptions === 'string'
+              ? [rawOptions]
+              : [];
         const cacheDir = this.cacheDir(outputFilename);
         try {
-            if (_.intersection(options!, ['js', 'check', '-c']).length > 0) filters.binary = false;
+            if (_.intersection(options, ['js', 'check', '-c']).length > 0) filters.binary = false;
             else {
                 filters.binary = true;
-                const objFile = unwrap(this.getCacheFile(options!, result.inputFilename!, cacheDir));
+                const objFile = unwrap(this.getCacheFile(options, result.inputFilename!, cacheDir));
                 if (await utils.fileExists(objFile)) {
                     await fs.rename(objFile, outputFilename);
                 } else {
EOF
@@ -97,13 +97,18 @@
outputFilename: string,
filters: ParseFiltersAndOutputOptions,
) {
const options = result.compilationOptions;
const rawOptions = result.compilationOptions;
const options: string[] = Array.isArray(rawOptions)
? rawOptions
: typeof rawOptions === 'string'
? [rawOptions]
: [];
const cacheDir = this.cacheDir(outputFilename);
try {
if (_.intersection(options!, ['js', 'check', '-c']).length > 0) filters.binary = false;
if (_.intersection(options, ['js', 'check', '-c']).length > 0) filters.binary = false;
else {
filters.binary = true;
const objFile = unwrap(this.getCacheFile(options!, result.inputFilename!, cacheDir));
const objFile = unwrap(this.getCacheFile(options, result.inputFilename!, cacheDir));
if (await utils.fileExists(objFile)) {
await fs.rename(objFile, outputFilename);
} else {
Copilot is powered by AI and may make mistakes. Always verify output.
@AnastaZIuk
Copy link
Member Author

wtf copilot why you review my pr

@AnastaZIuk AnastaZIuk merged commit 2d7c6c9 into main Dec 23, 2025
12 of 17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment