diff mbox series

[bpf-next,v2,4/5] bpf/verifier: relax MUL range computation check

Message ID 20240417122341.331524-5-cupertino.miranda@oracle.com (mailing list archive)
State Superseded
Delegated to: BPF
Headers show
Series bpf/verifier: range computation improvements | expand

Checks

Context Check Description
bpf/vmtest-bpf-next-PR success PR summary
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for bpf-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 955 this patch: 955
netdev/build_tools success No tools touched, skip
netdev/cc_maintainers warning 10 maintainers not CCed: john.fastabend@gmail.com jolsa@kernel.org sdf@google.com kpsingh@kernel.org daniel@iogearbox.net martin.lau@linux.dev haoluo@google.com andrii@kernel.org eddyz87@gmail.com song@kernel.org
netdev/build_clang success Errors and warnings before: 955 this patch: 955
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 966 this patch: 966
netdev/checkpatch fail CHECK: Using comparison to false is error prone ERROR: Unrecognized email address: 'Jose Marchesi <jose.marchesi@oracle.com' WARNING: Missing a blank line after declarations
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
bpf/vmtest-bpf-next-VM_Test-0 success Logs for Lint
bpf/vmtest-bpf-next-VM_Test-2 success Logs for Unittests
bpf/vmtest-bpf-next-VM_Test-5 success Logs for aarch64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-3 success Logs for Validate matrix.py
bpf/vmtest-bpf-next-VM_Test-1 success Logs for ShellCheck
bpf/vmtest-bpf-next-VM_Test-4 success Logs for aarch64-gcc / build / build for aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-10 success Logs for aarch64-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-12 success Logs for s390x-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-18 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-17 success Logs for s390x-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-19 success Logs for x86_64-gcc / build / build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-11 success Logs for s390x-gcc / build / build for s390x with gcc
bpf/vmtest-bpf-next-VM_Test-20 success Logs for x86_64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-28 success Logs for x86_64-llvm-17 / build / build for x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-29 success Logs for x86_64-llvm-17 / build-release / build for x86_64 with llvm-17 and -O2 optimization
bpf/vmtest-bpf-next-VM_Test-34 success Logs for x86_64-llvm-17 / veristat
bpf/vmtest-bpf-next-VM_Test-35 success Logs for x86_64-llvm-18 / build / build for x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-36 success Logs for x86_64-llvm-18 / build-release / build for x86_64 with llvm-18 and -O2 optimization
bpf/vmtest-bpf-next-VM_Test-42 success Logs for x86_64-llvm-18 / veristat
bpf/vmtest-bpf-next-VM_Test-6 success Logs for aarch64-gcc / test (test_maps, false, 360) / test_maps on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-9 success Logs for aarch64-gcc / test (test_verifier, false, 360) / test_verifier on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-7 success Logs for aarch64-gcc / test (test_progs, false, 360) / test_progs on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-8 success Logs for aarch64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-13 success Logs for s390x-gcc / test (test_maps, false, 360) / test_maps on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-16 success Logs for s390x-gcc / test (test_verifier, false, 360) / test_verifier on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-15 success Logs for s390x-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-14 success Logs for s390x-gcc / test (test_progs, false, 360) / test_progs on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-21 success Logs for x86_64-gcc / test (test_maps, false, 360) / test_maps on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-22 success Logs for x86_64-gcc / test (test_progs, false, 360) / test_progs on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-23 success Logs for x86_64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-24 success Logs for x86_64-gcc / test (test_progs_no_alu32_parallel, true, 30) / test_progs_no_alu32_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-25 success Logs for x86_64-gcc / test (test_progs_parallel, true, 30) / test_progs_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-26 success Logs for x86_64-gcc / test (test_verifier, false, 360) / test_verifier on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-27 success Logs for x86_64-gcc / veristat / veristat on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-30 success Logs for x86_64-llvm-17 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-31 success Logs for x86_64-llvm-17 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-32 success Logs for x86_64-llvm-17 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-33 success Logs for x86_64-llvm-17 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-38 success Logs for x86_64-llvm-18 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-37 success Logs for x86_64-llvm-18 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-40 success Logs for x86_64-llvm-18 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-39 success Logs for x86_64-llvm-18 / test (test_progs_cpuv4, false, 360) / test_progs_cpuv4 on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-41 success Logs for x86_64-llvm-18 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-18

Commit Message

Cupertino Miranda April 17, 2024, 12:23 p.m. UTC
MUL instruction required that src_reg would be a known value (i.e.
src_reg would be a const value). The condition in this case can be
relaxed, since multiplication is a cummutative operator and the range
computation is still valid if at least one of its registers is known.

Signed-off-by: Cupertino Miranda <cupertino.miranda@oracle.com>
Cc: Yonghong Song <yonghong.song@linux.dev>
Cc: Alexei Starovoitov <alexei.starovoitov@gmail.com>
Cc: David Faust <david.faust@oracle.com>
Cc: Jose Marchesi <jose.marchesi@oracle.com
Cc: Elena Zannoni <elena.zannoni@oracle.com>
---
 kernel/bpf/verifier.c | 19 ++++++++++++-------
 1 file changed, 12 insertions(+), 7 deletions(-)

Comments

Eduard Zingerman April 19, 2024, 2:30 a.m. UTC | #1
On Wed, 2024-04-17 at 13:23 +0100, Cupertino Miranda wrote:

[...]

>  static int is_safe_to_compute_dst_reg_range(struct bpf_insn *insn,
> +					    struct bpf_reg_state dst_reg,
>  					    struct bpf_reg_state src_reg)

Nit: there is no need to pass {dst,src}_reg by value,
     struct bpf_reg_state is 120 bytes in size
    (but maybe compiler handles this).

>  {
> -	bool src_known;
> +	bool src_known, dst_known;
>  	u64 insn_bitness = (BPF_CLASS(insn->code) == BPF_ALU64) ? 64 : 32;
>  	bool alu32 = (BPF_CLASS(insn->code) != BPF_ALU64);
>  	u8 opcode = BPF_OP(insn->code);
>  
> -	bool valid_known = true;
> -	src_known = is_const_reg_and_valid(src_reg, alu32, &valid_known);
> +	bool valid_known_src = true;
> +	bool valid_known_dst = true;
> +	src_known = is_const_reg_and_valid(src_reg, alu32, &valid_known_src);
> +	dst_known = is_const_reg_and_valid(dst_reg, alu32, &valid_known_dst);
>  
>  	/* Taint dst register if offset had invalid bounds
>  	 * derived from e.g. dead branches.
>  	 */
> -	if (valid_known == false)
> +	if (valid_known_src == false)
>  		return UNCOMPUTABLE_RANGE;
>  
>  	switch (opcode) {
> @@ -13457,10 +13460,12 @@ static int is_safe_to_compute_dst_reg_range(struct bpf_insn *insn,
>  	case BPF_OR:
>  		return COMPUTABLE_RANGE;
>  
> -	/* Compute range for the following only if the src_reg is known.
> +	/* Compute range for MUL if at least one of its registers is known.
>  	 */
>  	case BPF_MUL:
> -		return src_known ? COMPUTABLE_RANGE : UNCOMPUTABLE_RANGE;
> +		if (src_known || (dst_known && valid_known_dst))
> +			return COMPUTABLE_RANGE;
> +		break;

Is it even necessary to restrict src or dst to be known?
adjust_scalar_min_max_vals() logic for multiplication looks as follows:

	case BPF_MUL:
		dst_reg->var_off = tnum_mul(dst_reg->var_off, src_reg.var_off);
		scalar32_min_max_mul(dst_reg, &src_reg);
		scalar_min_max_mul(dst_reg, &src_reg);
		break;

Where tnum_mul() refers to a paper, and that paper does not restrict
abstract multiplication algorithm to constant values on either side.
The scalar_min_max_mul() and scalar32_min_max_mul() are similar:
- if both src and dst are positive
- if overflow is not possible
- adjust dst->min *= src->min
- adjust dst->max *= src->max

I think this should work just fine if neither of src or dst is a known constant.
What do you think?

[...]
Cupertino Miranda April 19, 2024, 9:47 a.m. UTC | #2
Eduard Zingerman writes:

> On Wed, 2024-04-17 at 13:23 +0100, Cupertino Miranda wrote:
>
> [...]
>
>>  static int is_safe_to_compute_dst_reg_range(struct bpf_insn *insn,
>> +					    struct bpf_reg_state dst_reg,
>>  					    struct bpf_reg_state src_reg)
>
> Nit: there is no need to pass {dst,src}_reg by value,
>      struct bpf_reg_state is 120 bytes in size
>     (but maybe compiler handles this).
>
>>  {
>> -	bool src_known;
>> +	bool src_known, dst_known;
>>  	u64 insn_bitness = (BPF_CLASS(insn->code) == BPF_ALU64) ? 64 : 32;
>>  	bool alu32 = (BPF_CLASS(insn->code) != BPF_ALU64);
>>  	u8 opcode = BPF_OP(insn->code);
>>
>> -	bool valid_known = true;
>> -	src_known = is_const_reg_and_valid(src_reg, alu32, &valid_known);
>> +	bool valid_known_src = true;
>> +	bool valid_known_dst = true;
>> +	src_known = is_const_reg_and_valid(src_reg, alu32, &valid_known_src);
>> +	dst_known = is_const_reg_and_valid(dst_reg, alu32, &valid_known_dst);
>>
>>  	/* Taint dst register if offset had invalid bounds
>>  	 * derived from e.g. dead branches.
>>  	 */
>> -	if (valid_known == false)
>> +	if (valid_known_src == false)
>>  		return UNCOMPUTABLE_RANGE;
>>
>>  	switch (opcode) {
>> @@ -13457,10 +13460,12 @@ static int is_safe_to_compute_dst_reg_range(struct bpf_insn *insn,
>>  	case BPF_OR:
>>  		return COMPUTABLE_RANGE;
>>
>> -	/* Compute range for the following only if the src_reg is known.
>> +	/* Compute range for MUL if at least one of its registers is known.
>>  	 */
>>  	case BPF_MUL:
>> -		return src_known ? COMPUTABLE_RANGE : UNCOMPUTABLE_RANGE;
>> +		if (src_known || (dst_known && valid_known_dst))
>> +			return COMPUTABLE_RANGE;
>> +		break;
>
> Is it even necessary to restrict src or dst to be known?
> adjust_scalar_min_max_vals() logic for multiplication looks as follows:
>
> 	case BPF_MUL:
> 		dst_reg->var_off = tnum_mul(dst_reg->var_off, src_reg.var_off);
> 		scalar32_min_max_mul(dst_reg, &src_reg);
> 		scalar_min_max_mul(dst_reg, &src_reg);
> 		break;
>
> Where tnum_mul() refers to a paper, and that paper does not restrict
> abstract multiplication algorithm to constant values on either side.
> The scalar_min_max_mul() and scalar32_min_max_mul() are similar:
> - if both src and dst are positive
> - if overflow is not possible
> - adjust dst->min *= src->min
> - adjust dst->max *= src->max
>
> I think this should work just fine if neither of src or dst is a known constant.
> What do you think?
>
With the refactor this looked like an armless change. Indeed if we agree
that the algorithm covers all scenarios, then why not.
I did not study the paper or the scalar_min_max_mul function nearly
enough to know for sure.
>
> [...]
Yonghong Song April 23, 2024, 8:53 p.m. UTC | #3
On 4/19/24 2:47 AM, Cupertino Miranda wrote:
> Eduard Zingerman writes:
>
>> On Wed, 2024-04-17 at 13:23 +0100, Cupertino Miranda wrote:
>>
>> [...]
>>
>>>   static int is_safe_to_compute_dst_reg_range(struct bpf_insn *insn,
>>> +					    struct bpf_reg_state dst_reg,
>>>   					    struct bpf_reg_state src_reg)
>> Nit: there is no need to pass {dst,src}_reg by value,
>>       struct bpf_reg_state is 120 bytes in size
>>      (but maybe compiler handles this).
>>
>>>   {
>>> -	bool src_known;
>>> +	bool src_known, dst_known;
>>>   	u64 insn_bitness = (BPF_CLASS(insn->code) == BPF_ALU64) ? 64 : 32;
>>>   	bool alu32 = (BPF_CLASS(insn->code) != BPF_ALU64);
>>>   	u8 opcode = BPF_OP(insn->code);
>>>
>>> -	bool valid_known = true;
>>> -	src_known = is_const_reg_and_valid(src_reg, alu32, &valid_known);
>>> +	bool valid_known_src = true;
>>> +	bool valid_known_dst = true;
>>> +	src_known = is_const_reg_and_valid(src_reg, alu32, &valid_known_src);
>>> +	dst_known = is_const_reg_and_valid(dst_reg, alu32, &valid_known_dst);
>>>
>>>   	/* Taint dst register if offset had invalid bounds
>>>   	 * derived from e.g. dead branches.
>>>   	 */
>>> -	if (valid_known == false)
>>> +	if (valid_known_src == false)
>>>   		return UNCOMPUTABLE_RANGE;
>>>
>>>   	switch (opcode) {
>>> @@ -13457,10 +13460,12 @@ static int is_safe_to_compute_dst_reg_range(struct bpf_insn *insn,
>>>   	case BPF_OR:
>>>   		return COMPUTABLE_RANGE;
>>>
>>> -	/* Compute range for the following only if the src_reg is known.
>>> +	/* Compute range for MUL if at least one of its registers is known.
>>>   	 */
>>>   	case BPF_MUL:
>>> -		return src_known ? COMPUTABLE_RANGE : UNCOMPUTABLE_RANGE;
>>> +		if (src_known || (dst_known && valid_known_dst))
>>> +			return COMPUTABLE_RANGE;
>>> +		break;
>> Is it even necessary to restrict src or dst to be known?
>> adjust_scalar_min_max_vals() logic for multiplication looks as follows:
>>
>> 	case BPF_MUL:
>> 		dst_reg->var_off = tnum_mul(dst_reg->var_off, src_reg.var_off);
>> 		scalar32_min_max_mul(dst_reg, &src_reg);
>> 		scalar_min_max_mul(dst_reg, &src_reg);
>> 		break;
>>
>> Where tnum_mul() refers to a paper, and that paper does not restrict
>> abstract multiplication algorithm to constant values on either side.
>> The scalar_min_max_mul() and scalar32_min_max_mul() are similar:
>> - if both src and dst are positive
>> - if overflow is not possible
>> - adjust dst->min *= src->min
>> - adjust dst->max *= src->max
>>
>> I think this should work just fine if neither of src or dst is a known constant.
>> What do you think?
>>
> With the refactor this looked like an armless change. Indeed if we agree
> that the algorithm covers all scenarios, then why not.
> I did not study the paper or the scalar_min_max_mul function nearly
> enough to know for sure.

I double checked and I think Eduard is correct. src_known checking
is not necessary for multiplication. It would be great if you can
add this change as well in the patch set.

>> [...]
Cupertino Miranda April 24, 2024, 2:59 p.m. UTC | #4
Yonghong Song writes:

> On 4/19/24 2:47 AM, Cupertino Miranda wrote:
>> Eduard Zingerman writes:
>>
>>> On Wed, 2024-04-17 at 13:23 +0100, Cupertino Miranda wrote:
>>>
>>> [...]
>>>
>>>>   static int is_safe_to_compute_dst_reg_range(struct bpf_insn *insn,
>>>> +					    struct bpf_reg_state dst_reg,
>>>>   					    struct bpf_reg_state src_reg)
>>> Nit: there is no need to pass {dst,src}_reg by value,
>>>       struct bpf_reg_state is 120 bytes in size
>>>      (but maybe compiler handles this).
>>>
>>>>   {
>>>> -	bool src_known;
>>>> +	bool src_known, dst_known;
>>>>   	u64 insn_bitness = (BPF_CLASS(insn->code) == BPF_ALU64) ? 64 : 32;
>>>>   	bool alu32 = (BPF_CLASS(insn->code) != BPF_ALU64);
>>>>   	u8 opcode = BPF_OP(insn->code);
>>>>
>>>> -	bool valid_known = true;
>>>> -	src_known = is_const_reg_and_valid(src_reg, alu32, &valid_known);
>>>> +	bool valid_known_src = true;
>>>> +	bool valid_known_dst = true;
>>>> +	src_known = is_const_reg_and_valid(src_reg, alu32, &valid_known_src);
>>>> +	dst_known = is_const_reg_and_valid(dst_reg, alu32, &valid_known_dst);
>>>>
>>>>   	/* Taint dst register if offset had invalid bounds
>>>>   	 * derived from e.g. dead branches.
>>>>   	 */
>>>> -	if (valid_known == false)
>>>> +	if (valid_known_src == false)
>>>>   		return UNCOMPUTABLE_RANGE;
>>>>
>>>>   	switch (opcode) {
>>>> @@ -13457,10 +13460,12 @@ static int is_safe_to_compute_dst_reg_range(struct bpf_insn *insn,
>>>>   	case BPF_OR:
>>>>   		return COMPUTABLE_RANGE;
>>>>
>>>> -	/* Compute range for the following only if the src_reg is known.
>>>> +	/* Compute range for MUL if at least one of its registers is known.
>>>>   	 */
>>>>   	case BPF_MUL:
>>>> -		return src_known ? COMPUTABLE_RANGE : UNCOMPUTABLE_RANGE;
>>>> +		if (src_known || (dst_known && valid_known_dst))
>>>> +			return COMPUTABLE_RANGE;
>>>> +		break;
>>> Is it even necessary to restrict src or dst to be known?
>>> adjust_scalar_min_max_vals() logic for multiplication looks as follows:
>>>
>>> 	case BPF_MUL:
>>> 		dst_reg->var_off = tnum_mul(dst_reg->var_off, src_reg.var_off);
>>> 		scalar32_min_max_mul(dst_reg, &src_reg);
>>> 		scalar_min_max_mul(dst_reg, &src_reg);
>>> 		break;
>>>
>>> Where tnum_mul() refers to a paper, and that paper does not restrict
>>> abstract multiplication algorithm to constant values on either side.
>>> The scalar_min_max_mul() and scalar32_min_max_mul() are similar:
>>> - if both src and dst are positive
>>> - if overflow is not possible
>>> - adjust dst->min *= src->min
>>> - adjust dst->max *= src->max
>>>
>>> I think this should work just fine if neither of src or dst is a known constant.
>>> What do you think?
>>>
>> With the refactor this looked like an armless change. Indeed if we agree
>> that the algorithm covers all scenarios, then why not.
>> I did not study the paper or the scalar_min_max_mul function nearly
>> enough to know for sure.
>
> I double checked and I think Eduard is correct. src_known checking
> is not necessary for multiplication. It would be great if you can
> add this change as well in the patch set.
Sure! Thanks for confirming this.
>
>>> [...]
diff mbox series

Patch

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index f410eb027e25..185ea7f19a79 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -13433,20 +13433,23 @@  enum {
 };
 
 static int is_safe_to_compute_dst_reg_range(struct bpf_insn *insn,
+					    struct bpf_reg_state dst_reg,
 					    struct bpf_reg_state src_reg)
 {
-	bool src_known;
+	bool src_known, dst_known;
 	u64 insn_bitness = (BPF_CLASS(insn->code) == BPF_ALU64) ? 64 : 32;
 	bool alu32 = (BPF_CLASS(insn->code) != BPF_ALU64);
 	u8 opcode = BPF_OP(insn->code);
 
-	bool valid_known = true;
-	src_known = is_const_reg_and_valid(src_reg, alu32, &valid_known);
+	bool valid_known_src = true;
+	bool valid_known_dst = true;
+	src_known = is_const_reg_and_valid(src_reg, alu32, &valid_known_src);
+	dst_known = is_const_reg_and_valid(dst_reg, alu32, &valid_known_dst);
 
 	/* Taint dst register if offset had invalid bounds
 	 * derived from e.g. dead branches.
 	 */
-	if (valid_known == false)
+	if (valid_known_src == false)
 		return UNCOMPUTABLE_RANGE;
 
 	switch (opcode) {
@@ -13457,10 +13460,12 @@  static int is_safe_to_compute_dst_reg_range(struct bpf_insn *insn,
 	case BPF_OR:
 		return COMPUTABLE_RANGE;
 
-	/* Compute range for the following only if the src_reg is known.
+	/* Compute range for MUL if at least one of its registers is known.
 	 */
 	case BPF_MUL:
-		return src_known ? COMPUTABLE_RANGE : UNCOMPUTABLE_RANGE;
+		if (src_known || (dst_known && valid_known_dst))
+			return COMPUTABLE_RANGE;
+		break;
 
 	/* Shift operators range is only computable if shift dimension operand
 	 * is known. Also, shifts greater than 31 or 63 are undefined. This
@@ -13493,7 +13498,7 @@  static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env,
 	bool alu32 = (BPF_CLASS(insn->code) != BPF_ALU64);
 	int ret;
 
-	int is_safe = is_safe_to_compute_dst_reg_range(insn, src_reg);
+	int is_safe = is_safe_to_compute_dst_reg_range(insn, *dst_reg, src_reg);
 	switch (is_safe) {
 	case UNCOMPUTABLE_RANGE:
 		__mark_reg_unknown(env, dst_reg);