Module: OneGadget::Emulators::Constraints
- Included in:
- Processor
- Defined in:
- lib/one_gadget/emulators/constraints.rb
Overview
What a candidate requires of its caller, collected while it is emulated and rendered once it ends. A path that dereferences a pointer, stores through an address, or reaches a call that reads one records the requirement here rather than assuming it holds; #constraints then drops the ones another already implies and names what is left. Mixed into Processor.
Constant Summary collapse
- CLOBBERED =
Marks a register holding whatever a call returned or left behind; see Processor#clobber_caller_saved.
'$clobbered'- ADDRESS_TYPES =
Constraint types whose payload is an address Lambda asserting the target is mapped --
:writable(a store target) and:readable(an unconditional dereference, see #finalize_deferred_reads). Both are keyed, offset- normalised, and imply non-NULL identically; they differ only in how they render (see #render_constraint). The remaining type,:raw, carries a ready-made constraint string that keys on itself, and:cmpa comparison recorded as its[lhs, operator, rhs]parts (see OneGadget::Emulators::Conditional), so it can be inspected rather than re-parsed from the rendered text. i[writable readable].freeze
- POINTER_REQUIREMENTS =
SafeCalls requirements naming what a callee does with a pointer argument, each recorded as something the caller must arrange (see #record_pointer), as opposed to a precondition read off the value as it stands.
i[writable deref nullable_deref null].freeze
- NULLABLE_REQUIREMENTS =
The POINTER_REQUIREMENTS a NULL argument already satisfies: both ask for a pointer the callee will leave alone, and NULL is how that is asked for.
i[nullable_deref null].freeze
Instance Method Summary collapse
-
#address_deref0?(type, obj) ⇒ Boolean
Whether
(type, obj)is an address constraint on a bare (deref-0) target, i.e. -
#closed_fds ⇒ Array<String>
Where each descriptor this candidate closes is read from, in the order they are closed, without repeats.
-
#constraint_key(type, obj) ⇒ Object
De-duplication key: an address constraint collapses per (type, base) so constraints of different types on the same register stay distinct; a raw constraint keys on its own text.
-
#constraints ⇒ Array<String>
Extra constraints found during execution.
-
#drop_implied_nonzero(cons) ⇒ Array<[Symbol, Object]>
Drop a "
!= 0x0" branch constraint that another constraint already implies: an address constraint (+writable: +imm+ store target, or readable: <reg>) forcesto be a valid (mapped, non-NULL) pointer, so a NULL-check branch on the same register adds nothing. -
#drop_restated_null(cons) ⇒ Array<[Symbol, Object]>
Drop a "
== 0x0" branch constraint that a NULL requirement on the same value already states (see #require_null). -
#render_constraint(type, obj) ⇒ String
Render a constraint to its output string.
Instance Method Details
#address_deref0?(type, obj) ⇒ Boolean
Whether (type, obj) is an address constraint on a bare (deref-0) target,
i.e. one carrying a base register and offset to normalise.
64 65 66 |
# File 'lib/one_gadget/emulators/constraints.rb', line 64 def address_deref0?(type, obj) ADDRESS_TYPES.include?(type) && obj.deref_count.zero? end |
#closed_fds ⇒ Array<String>
39 40 41 |
# File 'lib/one_gadget/emulators/constraints.rb', line 39 def closed_fds @closed_fds.uniq end |
#constraint_key(type, obj) ⇒ Object
De-duplication key: an address constraint collapses per (type, base) so constraints of different types on the same register stay distinct; a raw constraint keys on its own text.
74 75 76 77 78 |
# File 'lib/one_gadget/emulators/constraints.rb', line 74 def constraint_key(type, obj) return obj unless ADDRESS_TYPES.include?(type) [type, obj.deref_count.zero? ? obj.obj.to_s : obj.to_s] end |
#constraints ⇒ Array<String>
45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/one_gadget/emulators/constraints.rb', line 45 def constraints finalize_deferred_reads return [] if @constraints.empty? # An address constraint is keyed by its base register (deref-0) or full # expression (compound); several through one base (e.g. stores at reg+0x0 # and reg+0x8) impose the same requirement, so keep just the smallest # offset (sort ascending, then uniq keeps that first). cons = @constraints.sort_by { |type, obj| address_deref0?(type, obj) ? obj.immi : 0 } .uniq { |type, obj| constraint_key(type, obj) } cons = drop_restated_null(drop_implied_nonzero(cons)) cons.map { |type, obj| render_constraint(type, obj) }.sort end |
#drop_implied_nonzero(cons) ⇒ Array<[Symbol, Object]>
Drop a "readable: <reg>) forces
100 101 102 103 104 105 106 107 108 109 |
# File 'lib/one_gadget/emulators/constraints.rb', line 100 def drop_implied_nonzero(cons) nonzero_regs = cons.filter_map do |type, obj| obj.obj.to_s if address_deref0?(type, obj) end return cons if nonzero_regs.empty? cons.reject do |type, obj| type == :cmp && obj[1] == '!=' && obj[2] == Conditional::ZERO && nonzero_regs.include?(obj[0]) end end |
#drop_restated_null(cons) ⇒ Array<[Symbol, Object]>
Drop a "
116 117 118 119 120 121 122 123 |
# File 'lib/one_gadget/emulators/constraints.rb', line 116 def drop_restated_null(cons) nulls = cons.filter_map { |type, obj| obj[/\A(.+) == NULL\z/, 1] if type == :raw } return cons if nulls.empty? cons.reject do |type, obj| type == :cmp && obj[1] == '==' && obj[2] == Conditional::ZERO && nulls.include?(obj[0]) end end |
#render_constraint(type, obj) ⇒ String
Render a constraint to its output string.
84 85 86 87 88 89 90 91 |
# File 'lib/one_gadget/emulators/constraints.rb', line 84 def render_constraint(type, obj) case type when :writable then "writable: #{obj}" when :readable then "readable: #{obj}" when :cmp then obj.join(' ') else obj end end |