logo raviqqe's logbook

Security audit of Melior

Summary

Scope and method

Severity

SeverityMeaning
CriticalMemory corruption reachable from ordinary safe code
HighMemory corruption reachable from safe code through common usage of an API
MediumMemory corruption, a data race or a process abort reachable from less common safe usage
LowWrong results, leaks or panics in safe APIs
InformationalHardening, documentation and maintenance issues

Overview

PackageCriticalHighMediumLowInformational
melior01745
melior-macro00044

Fixed findings

IDFindingFixed by
C1IR handle lifetimes weren’t tied to their owners or contexts#999, #1000
H1MLIR kept strings borrowed from Rust after the borrows ended#1004
H2Indices and slice lengths went to MLIR unchecked#1005
H3Dense elements attributes didn’t validate counts, widths or kinds#1006
H4Mutable operation handles were copyable and convertible into owned operations#982, #1001
H5Successor blocks were stored without a lifetime#1007 (made unsafe)
H6The greedy rewrite driver erased operations behind shared borrows#1013
H8Callbacks that MLIR keeps had no Send or 'static bounds#1012
H9Shared block handles gave mutable operation handles to the operation-level rewrite drivers#1015 (made unsafe)
M4String references mishandled empty strings and trailing NULs#1019
M9Walk callbacks couldn’t use data from the caller’s context (a regression from #999)#1009
X-M1Builders of operations with attribute-sized segments never set segment sizes#922
X-M2Accessors of empty variadic groups overflowed in debug builds#954
X-M3Accessors of same-size variadic groups computed wrong group lengths#966
—Leaks of frozen pattern sets, closures and external pass state, and panics in pattern callbacks#956, #980, #982

melior

High

H7: Erasing rewriter methods are safe, and a pattern that rewrites and then reports no match makes MLIR retry an erased root

Medium

M1: Functions don’t check the kinds of their arguments

M2: Structural operations on detached or malformed IR dereference null

M3: ExecutionEngine::new ignores a null result

M5: IR that isn’t valid UTF-8 aborts the process inside diagnostic handlers

M6: Pass registration is safe to call but not synchronized

M7: Iterators and walks fetch the next element in advance

M8: Callbacks can be re-entered or freed while they are running

Low

L1: Unconsumed builders and patterns leak

L2: Wrong results instead of errors

L3: Panics in safe APIs

L4: A pass nested under the wrong operation aborts the process

Informational

I1: Layout assumptions

The Deref implementations of BlockRef, RegionRef and OperationRefMut, and the to_ref methods of OperationRef and ContextRef, transmute reference handles into their owned types (melior/src/ir/operation.rs:163, :279, melior/src/ir/block.rs:179, melior/src/ir/region.rs:111, melior/src/context.rs:220). They rely on both structs having the same layout, which neither type guarantees. OperationBuilder::add_regions_vec transmutes Vec<Region> into Vec<ManuallyDrop<Region>> (melior/src/ir/operation/builder.rs:87). #1003 made the handle types passed to C as arrays #[repr(transparent)].

I2: Preconditions only in doc comments

Some safe functions state preconditions only in their documentation, such as a value being of a specific kind. M1 lists the ones that crash.

I3: Dynamic index sentinel collision

GepIndex::Const(i32::MIN) (melior/src/helpers/llvm.rs:19) collides with the value MLIR uses to mark dynamic indices, so that constant index is read as a dynamic one.

I4: Mismatches the types don’t catch

I5: The README’s safety section is out of date

The section’s example, invalidation of references returned by Region::append_block, has been a compile error since #1000, and the section doesn’t mention H7, M1, M2, M7 or M8. An update (#1017) was closed without merging.

melior-macro

Low

X-L1: The SameOperandsAndResultType builder panics on an empty first variadic operand

X-L2: The setter of an optional FirstAttrDerivedResultType attribute adds results on every call

X-L3: Getters of optional elements in SameVariadic*Size operations don’t compile

X-L4: Unnamed operands are dropped before operand positions are computed

Informational

X-I1: Accessor arithmetic can underflow on malformed operations

Typed operations only check the operation name on conversion, so for an unverified operation with too few operands, expressions such as count() + 1 - length (macro/src/dialect/generation/element_accessor.rs:34, :41) underflow. Debug builds panic, and release builds wrap and return errors.

X-I2: Build tracking and naming

include_directory_env_vars is read with env::var (macro/src/dialect.rs:37) without being tracked as a build input, so changing the variable leaves a stale expansion. map_name (macro/src/utility.rs:10) produces inconsistent names, such as is_float_4_e_2_m_1_fn next to is_float8e4m3fn.

X-I3: The build script runs llvm-config through a shell with an unquoted path

macro/build.rs:38 runs sh -c with the llvm-config path interpolated unquoted, so an installation path containing spaces breaks the build.

X-I4: A generated local variable shadows fields of the same name

The setters of inferred result types bind a local variable named result_type (macro/src/dialect/generation/operation_builder.rs:234, :259) with a call site span, so an operand or attribute named result_type is shadowed by it. r47 fails to compile with E0308. #947 fixed the same problem for sizes with a mixed site span.

Reproduction status

ResultReproductions
Segmentation faultr08, r10, r11, r12, r18, r22, r28, r38, r41, r44, r45, r54
Abortr32, r39
Panicr36, r46, r48
Wrong resultr22, r23, r36, r46, r48, r55
No longer compilesr01, r02, r06, r07, r09, r19, r20, r24, r37, r42, r43, r51, r52, r53
Fails to compiler47 (X-I4)
Fixed at run timer03, r04, r05, r13, r14, r15, r16, r17, r25, r26, r33, r34, r35, r40, r49
No issue foundr21, r50 (diagnostic handlers run only on the context’s thread)

r43, r52 and r53 no longer compile without unsafe since #1015. The other programs that no longer compile were fixed by the lifetime, bound and borrow changes listed in fixed findings.

Recommendations

  1. Fix H7, the only remaining High finding, by finishing #1014.
  2. Fix M8 and M7, the remaining use-after-free reachable from safe code without malformed IR, with re-entrancy guards in the callback trampolines and documented or enforced iteration rules.
  3. Add kind and attachment checks for M1, M2 and M3. They are local validation changes like the ones in #1005 and #1006.
  4. Convert printed IR lossily for M5, since it affects tools that process untrusted input.
  5. Decide how far the registry lock for M6 should reach.
  6. Fix X-L4 by naming unnamed operands positionally, as #974 did for results.