@@ -417,56 +417,34 @@ class BaseCallVariable extends AbstractBaseSourceVariable, TBaseCallVariable {
417
417
override CppType getLanguageType ( ) { result = getResultLanguageType ( call ) }
418
418
}
419
419
420
- /**
421
- * Holds if the value pointed to by `operand` can potentially be
422
- * modified be the caller.
423
- */
424
- predicate isModifiableByCall ( ArgumentOperand operand , int indirectionIndex ) {
425
- exists ( CallInstruction call , int index , CppType type |
426
- indirectionIndex = [ 1 .. countIndirectionsForCppType ( type ) ] and
427
- type = getLanguageType ( operand ) and
428
- call .getArgumentOperand ( index ) = operand and
429
- if index = - 1
430
- then
431
- // A qualifier is "modifiable" if:
432
- // 1. the member function is not const specified, or
433
- // 2. the member function is `const` specified, but returns a pointer or reference
434
- // type that is non-const.
435
- //
436
- // To see why this is necessary, consider the following function:
437
- // ```
438
- // struct C {
439
- // void* data_;
440
- // void* data() const { return data; }
441
- // };
442
- // ...
443
- // C c;
444
- // memcpy(c.data(), source, 16)
445
- // ```
446
- // the data pointed to by `c.data_` is potentially modified by the call to `memcpy` even though
447
- // `C::data` has a const specifier. So we further place the restriction that the type returned
448
- // by `call` should not be of the form `const T*` (for some deeply const type `T`).
449
- if call .getStaticCallTarget ( ) instanceof Cpp:: ConstMemberFunction
450
- then
451
- exists ( PointerOrArrayOrReferenceType resultType |
452
- resultType = call .getResultType ( ) and
453
- not resultType .isDeeplyConstBelow ( )
454
- )
455
- else any ( )
456
- else
457
- // An argument is modifiable if it's a non-const pointer or reference type.
458
- isModifiableAt ( type , indirectionIndex )
459
- )
460
- }
461
-
462
- /**
463
- * Holds if `t` is a pointer or reference type that supports at least `indirectionIndex` number
464
- * of indirections, and the `indirectionIndex` indirection cannot be modfiied by passing a
465
- * value of `t` to a function.
466
- */
467
- private predicate isModifiableAtImpl ( CppType cppType , int indirectionIndex ) {
468
- indirectionIndex = [ 1 .. countIndirectionsForCppType ( cppType ) ] and
469
- (
420
+ private module IsModifiableAtImpl {
421
+ /**
422
+ * Holds if the `indirectionIndex`'th dereference of a value of type
423
+ * `cppType` is a type that can be modified (either by modifying the value
424
+ * itself or one of its fields if it's a class type).
425
+ *
426
+ * For example, a value of type `const int* const` cannot be modified
427
+ * at any indirection index (because it's a constant pointer to constant
428
+ * data), and a value of type `int *const *` is modifiable at indirection index
429
+ * 2 only.
430
+ *
431
+ * A value of type `const S2* s2` where `s2` is
432
+ * ```cpp
433
+ * struct S { int x; }
434
+ * ```
435
+ * can be modified at indirection index 1. This is to ensure that we generate
436
+ * a `PostUpdateNode` for the argument corresponding to the `s2` parameter in
437
+ * an example such as:
438
+ * ```cpp
439
+ * void set_field(const S2* s2)
440
+ * {
441
+ * s2->s->x = 42;
442
+ * }
443
+ * ```
444
+ */
445
+ bindingset [ cppType, indirectionIndex]
446
+ pragma [ inline_late]
447
+ private predicate impl ( CppType cppType , int indirectionIndex ) {
470
448
exists ( Type pointerType , Type base , Type t |
471
449
pointerType = t .getUnderlyingType ( ) and
472
450
pointerType = any ( Indirection ind ) .getUnderlyingType ( ) and
@@ -480,29 +458,115 @@ private predicate isModifiableAtImpl(CppType cppType, int indirectionIndex) {
480
458
// one of the members was modified.
481
459
exists ( base .stripType ( ) .( Cpp:: Class ) .getAField ( ) )
482
460
)
461
+ }
462
+
463
+ /**
464
+ * Holds if `cppType` is modifiable with an indirection index of at least 1.
465
+ *
466
+ * This predicate factored out into a separate predicate for two reasons:
467
+ * - This predicate needs to be recursive because, if a type is modifiable
468
+ * at indirection `i`, then it's also modifiable at indirection index `i+1`
469
+ * (because the pointer could be completely re-assigned at indirection `i`).
470
+ * - We special-case indirection index `0` so that pointer arguments that can
471
+ * be modified at some index always have a `PostUpdateNode` at indiretion
472
+ * index 0 even though the 0'th indirection can never be modified by a
473
+ * callee.
474
+ */
475
+ private predicate isModifiableAtImplAtLeast1 ( CppType cppType , int indirectionIndex ) {
476
+ indirectionIndex = [ 1 .. countIndirectionsForCppType ( cppType ) ] and
477
+ (
478
+ impl ( cppType , indirectionIndex )
479
+ or
480
+ // If the `indirectionIndex`'th dereference of a type can be modified
481
+ // then so can the `indirectionIndex + 1`'th dereference.
482
+ isModifiableAtImplAtLeast1 ( cppType , indirectionIndex - 1 )
483
+ )
484
+ }
485
+
486
+ /**
487
+ * Holds if `cppType` is modifiable at indirection index 0.
488
+ *
489
+ * In reality, the 0'th indirection of a pointer (i.e., the pointer itself)
490
+ * can never be modified by a callee, but it is sometimes useful to be able
491
+ * to specify the value of the pointer, as its coming out of a function, as
492
+ * a source of dataflow since the shared library's reverse-read mechanism
493
+ * then ensures that field-flow is accounted for.
494
+ */
495
+ private predicate isModifiableAtImplAt0 ( CppType cppType ) { impl ( cppType , 0 ) }
496
+
497
+ /**
498
+ * Holds if `t` is a pointer or reference type that supports at least
499
+ * `indirectionIndex` number of indirections, and the `indirectionIndex`
500
+ * indirection cannot be modfiied by passing a value of `t` to a function.
501
+ */
502
+ private predicate isModifiableAtImpl ( CppType cppType , int indirectionIndex ) {
503
+ isModifiableAtImplAtLeast1 ( cppType , indirectionIndex )
483
504
or
484
- // If the `indirectionIndex`'th dereference of a type can be modified
485
- // then so can the `indirectionIndex + 1`'th dereference.
486
- isModifiableAtImpl ( cppType , indirectionIndex - 1 )
487
- )
488
- }
505
+ indirectionIndex = 0 and
506
+ isModifiableAtImplAt0 ( cppType )
507
+ }
489
508
490
- /**
491
- * Holds if `t` is a type with at least `indirectionIndex` number of indirections,
492
- * and the `indirectionIndex` indirection can be modified by passing a value of
493
- * type `t` to a function function.
494
- */
495
- bindingset [ indirectionIndex]
496
- predicate isModifiableAt ( CppType cppType , int indirectionIndex ) {
497
- isModifiableAtImpl ( cppType , indirectionIndex )
498
- or
499
- exists ( PointerWrapper pw , Type t |
500
- cppType .hasType ( t , _) and
501
- t .stripType ( ) = pw and
502
- not pw .pointsToConst ( )
503
- )
509
+ /**
510
+ * Holds if `t` is a type with at least `indirectionIndex` number of
511
+ * indirections, and the `indirectionIndex` indirection can be modified by
512
+ * passing a value of type `t` to a function function.
513
+ */
514
+ bindingset [ indirectionIndex]
515
+ predicate isModifiableAt ( CppType cppType , int indirectionIndex ) {
516
+ isModifiableAtImpl ( cppType , indirectionIndex )
517
+ or
518
+ exists ( PointerWrapper pw , Type t |
519
+ cppType .hasType ( t , _) and
520
+ t .stripType ( ) = pw and
521
+ not pw .pointsToConst ( )
522
+ )
523
+ }
524
+
525
+ /**
526
+ * Holds if the value pointed to by `operand` can potentially be
527
+ * modified be the caller.
528
+ */
529
+ predicate isModifiableByCall ( ArgumentOperand operand , int indirectionIndex ) {
530
+ exists ( CallInstruction call , int index , CppType type |
531
+ indirectionIndex = [ 0 .. countIndirectionsForCppType ( type ) ] and
532
+ type = getLanguageType ( operand ) and
533
+ call .getArgumentOperand ( index ) = operand and
534
+ if index = - 1
535
+ then
536
+ // A qualifier is "modifiable" if:
537
+ // 1. the member function is not const specified, or
538
+ // 2. the member function is `const` specified, but returns a pointer or reference
539
+ // type that is non-const.
540
+ //
541
+ // To see why this is necessary, consider the following function:
542
+ // ```
543
+ // struct C {
544
+ // void* data_;
545
+ // void* data() const { return data; }
546
+ // };
547
+ // ...
548
+ // C c;
549
+ // memcpy(c.data(), source, 16)
550
+ // ```
551
+ // the data pointed to by `c.data_` is potentially modified by the call to `memcpy` even though
552
+ // `C::data` has a const specifier. So we further place the restriction that the type returned
553
+ // by `call` should not be of the form `const T*` (for some deeply const type `T`).
554
+ if call .getStaticCallTarget ( ) instanceof Cpp:: ConstMemberFunction
555
+ then
556
+ exists ( PointerOrArrayOrReferenceType resultType |
557
+ resultType = call .getResultType ( ) and
558
+ not resultType .isDeeplyConstBelow ( )
559
+ )
560
+ else any ( )
561
+ else
562
+ // An argument is modifiable if it's a non-const pointer or reference type.
563
+ isModifiableAt ( type , indirectionIndex )
564
+ )
565
+ }
504
566
}
505
567
568
+ import IsModifiableAtImpl
569
+
506
570
abstract class BaseSourceVariableInstruction extends Instruction {
507
571
/** Gets the base source variable accessed by this instruction. */
508
572
abstract BaseSourceVariable getBaseSourceVariable ( ) ;
0 commit comments