Skip to content

Commit 33ebb4a

Browse files
committed
Covert clang ir types to c type string for pattern matching
1 parent 80b450b commit 33ebb4a

11 files changed

Lines changed: 465 additions & 21 deletions

docs/GettingStarted/patch_specifications.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -172,22 +172,21 @@ For operation-based matching, the following additional fields are available:
172172
| Field | Description | Example |
173173
|-------|-------------|---------|
174174
| `name` | Function name pattern (supports regex with `/pattern/`) | `"/.*secure.*/`, `"authenticate"` |
175-
| `type` | Function type pattern (optional) | `"!cir.func<!cir.void ()>"` |
176175

177176
#### Symbol Match Fields (Operation-Based Matching)
178177

179178
| Field | Description | Example |
180179
|-------|-------------|---------|
181180
| `name` | Symbol name pattern (supports regex with `/pattern/`) | `"/.*password.*/`, `"secret_key"` |
182-
| `type` | Symbol type pattern (optional) | `"!cir.ptr<!cir.int<u, 32>>"` |
181+
| `type` | Symbol type pattern (optional) | `"int32*"` |
183182

184183
#### Operand Match Fields (Operation-Based Matching)
185184

186185
| Field | Description | Example |
187186
|-------|-------------|---------|
188187
| `index` | Position of the operand (0-based) | `0` (first operand), `1` (second operand) |
189188
| `name` | Name of the operand variable | `"addr"`, `"/.*buffer.*/` |
190-
| `type` | Type of the operand | `"!cir.ptr<!cir.int<u, 32>>"` |
189+
| `type` | Type of the operand | `"int32*"` |
191190

192191
Common operand patterns:
193192
- `cir.load`: operand 0 = address to load from
@@ -208,7 +207,7 @@ Common operand patterns:
208207
| Field | Description | Example |
209208
|-------|-------------|---------|
210209
| `name` | Variable name pattern (supports regex with `/pattern/`) | `"/.*password.*/`, `"secret_key"` |
211-
| `type` | Variable type pattern (optional) | `"!cir.ptr<!cir.int<u, 32>>"` |
210+
| `type` | Variable type pattern (optional) | `"struct struct_anon_struct_4_1_58265f66*"` |
212211

213212
### Action Fields
214213

@@ -378,6 +377,7 @@ meta_patches:
378377
argument_matches:
379378
- index: 0
380379
name: "usb_g"
380+
type: "struct struct_anon_struct_4_1_58265f66*"
381381
382382
action:
383383
- mode: "apply_before"
@@ -409,7 +409,7 @@ meta_contracts:
409409
- name: "authenticate" # Exact function name
410410
symbol_matches:
411411
- name: "/.*password.*/" # Variables containing "password"
412-
type: "!cir.ptr<!cir.int<u, 32>>"
412+
type: "int32*"
413413
```
414414

415415
### Example 2: Match Store Operations with Specific Operands
@@ -423,10 +423,10 @@ meta_contracts:
423423
operand_matches:
424424
- index: 0 # The value being stored (first operand)
425425
name: "user_input"
426-
type: "!cir.ptr<!cir.char>"
426+
type: "char*"
427427
- index: 1 # The address being stored to (second operand)
428428
name: "buffer"
429-
type: "!cir.ptr<!cir.array<!cir.char x 256>>"
429+
type: "char[256]"
430430
```
431431

432432
### Pattern Matching

include/patchestry/Passes/OperationMatcher.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,15 @@ namespace patchestry::passes {
192192
*/
193193
static std::string extract_ssa_value_name(mlir::Value value); // NOLINT
194194

195+
/**
196+
* @brief Extracts the type from an SSA value, following the definition chain.
197+
*
198+
* @param value The SSA value to extract type from
199+
* @return The variable type, following through operations that may modify the original
200+
* type
201+
*/
202+
static mlir::Type extract_ssa_value_type(mlir::Value value); // NOLINT
203+
195204
/**
196205
* @brief Extracts symbol name from operation attributes.
197206
*
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright (c) 2025, Trail of Bits, Inc.
3+
*
4+
* This source code is licensed in accordance with the terms specified in
5+
* the LICENSE file found in the root directory of this source tree.
6+
*/
7+
8+
#pragma once
9+
10+
#include <string>
11+
12+
namespace mlir {
13+
class MLIRContext;
14+
class Type;
15+
} // namespace mlir
16+
17+
namespace patchestry {
18+
namespace utils {
19+
20+
/// Convert C-like type names to CIR types
21+
mlir::Type convertCTypesToCIRTypes(mlir::MLIRContext *context, std::string type_name);
22+
23+
/// Convert CIR type back to C-like type name string
24+
std::string convertCIRTypesToCTypes(mlir::Type cir_type);
25+
26+
} // namespace utils
27+
} // namespace patchestry

lib/patchestry/Passes/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ add_library(patchestry_passes STATIC
77
InstrumentationPass.cpp
88
Compiler.cpp
99
OperationMatcher.cpp
10+
Utils.cpp
1011
)
1112

1213
set(CLANG_IR_LIBS

lib/patchestry/Passes/OperationMatcher.cpp

Lines changed: 64 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#include <mlir/IR/Operation.h>
1818
#include <mlir/IR/SymbolTable.h>
1919

20+
#include <patchestry/Passes/Utils.hpp>
21+
2022
namespace patchestry::passes {
2123

2224
bool OperationMatcher::matches(
@@ -133,19 +135,12 @@ namespace patchestry::passes {
133135
}
134136

135137
std::string func_name = func.getName().str();
136-
std::string func_type = type_to_string(func.getFunctionType());
137-
138138
for (const auto &context : function_context) {
139139
// Check function name match
140140
if (!matches_pattern(func_name, context.name)) {
141141
continue;
142142
}
143143

144-
// Check function type match if specified
145-
if (!context.type.empty() && !matches_pattern(func_type, context.type)) {
146-
continue;
147-
}
148-
149144
return true;
150145
}
151146

@@ -180,7 +175,8 @@ namespace patchestry::passes {
180175

181176
// Check argument type if specified
182177
if (!operand_match.type.empty()) {
183-
if (!matches_type(operand.getType(), operand_match.type)) {
178+
auto variable_type = extract_ssa_value_type(operand);
179+
if (!matches_type(variable_type, operand_match.type)) {
184180
return false;
185181
}
186182
}
@@ -222,7 +218,7 @@ namespace patchestry::passes {
222218
auto result = results[i];
223219
std::string var_name =
224220
extract_variable_name(op, static_cast< unsigned >(operands.size() + i));
225-
std::string var_type = type_to_string(result.getType());
221+
std::string var_type = utils::convertCIRTypesToCTypes(result.getType());
226222

227223
for (const auto &var_match : symbol_matches) {
228224
bool name_matches =
@@ -267,7 +263,13 @@ namespace patchestry::passes {
267263

268264
// Check argument type if specified
269265
if (!arg_match.type.empty()) {
270-
if (!matches_type(operand.getType(), arg_match.type)) {
266+
auto variable_type = extract_ssa_value_type(operand);
267+
// Note: The type matching here is relaxed and checks for matching types both
268+
// following the chain of operations or the direct type of the operand; if the
269+
// match is found with any one, it will go ahead with patching.
270+
if (!matches_type(variable_type, arg_match.type)
271+
&& !matches_type(operand.getType(), arg_match.type))
272+
{
271273
return false;
272274
}
273275
}
@@ -289,7 +291,7 @@ namespace patchestry::passes {
289291
for (unsigned i = 0; i < operands.size(); ++i) {
290292
auto operand = operands[i];
291293
std::string var_name = extract_variable_name(op, i);
292-
std::string var_type = type_to_string(operand.getType());
294+
std::string var_type = utils::convertCIRTypesToCTypes(operand.getType());
293295

294296
for (const auto &var_match : variable_matches) {
295297
bool name_matches =
@@ -356,7 +358,8 @@ namespace patchestry::passes {
356358
return true;
357359
}
358360

359-
std::string type_str = type_to_string(type);
361+
// convert mlir types to c types
362+
std::string type_str = utils::convertCIRTypesToCTypes(type);
360363
return matches_pattern(type_str, type_pattern);
361364
}
362365

@@ -431,6 +434,55 @@ namespace patchestry::passes {
431434
return "";
432435
}
433436

437+
mlir::Type OperationMatcher::extract_ssa_value_type(mlir::Value value) {
438+
// Check if the value is defined by an operation that might modify the original type
439+
if (auto defining_op = value.getDefiningOp()) {
440+
// For cast operations, try to get the original type
441+
if (auto cast_op = mlir::dyn_cast< cir::CastOp >(defining_op)) {
442+
// Follow the cast chain to get the original type
443+
auto src_value = cast_op.getSrc();
444+
return extract_ssa_value_type(src_value);
445+
}
446+
447+
// For load operations, get the pointed-to type
448+
if (auto load_op = mlir::dyn_cast< cir::LoadOp >(defining_op)) {
449+
auto ptr_value = load_op.getAddr();
450+
auto ptr_type = ptr_value.getType();
451+
if (auto cir_ptr_type = mlir::dyn_cast< cir::PointerType >(ptr_type)) {
452+
return cir_ptr_type.getPointee();
453+
}
454+
}
455+
456+
// For get member operations, try to get the member type
457+
if (auto get_member_op = mlir::dyn_cast< cir::GetMemberOp >(defining_op)) {
458+
// Return the type of the member being accessed
459+
return get_member_op.getType();
460+
}
461+
462+
// For other operations, recursively check operands
463+
auto operands = defining_op->getOperands();
464+
if (!operands.empty()) {
465+
// For simple operations, follow the first operand
466+
return extract_ssa_value_type(operands[0]);
467+
}
468+
}
469+
470+
// For block arguments, check if they represent function parameters with original types
471+
if (auto block_arg = mlir::dyn_cast< mlir::BlockArgument >(value)) {
472+
auto block = block_arg.getOwner();
473+
if (auto parent_op = block->getParentOp()) {
474+
// For function arguments, the block argument type should be the actual
475+
// parameter type
476+
if (auto func_op = mlir::dyn_cast< cir::FuncOp >(parent_op)) {
477+
return block_arg.getType();
478+
}
479+
}
480+
}
481+
482+
// Default: return the direct type of the value
483+
return value.getType();
484+
}
485+
434486
std::string OperationMatcher::extract_symbol_name(mlir::Operation *op) {
435487
// Check for standard symbol attributes
436488
if (auto symbol_name =

0 commit comments

Comments
 (0)