Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ICmp does not work with signed values #153

Open
0xThemis opened this issue Nov 21, 2023 · 0 comments
Open

ICmp does not work with signed values #153

0xThemis opened this issue Nov 21, 2023 · 0 comments
Labels
bug Something isn't working

Comments

@0xThemis
Copy link

During the ICMP handling, the parser behaves incorrectly if one of the operands is negative (additionally, it does not constrain anything at the moment). The problem arises because the comparison simply compares the underlying field elements by "<", ">" etc. For negative values, which are greater than modulus / 2, this yields a wrong result.

The code responsible can be seen here:

            template<typename map_type>
            void handle_scalar_cmp(const llvm::ICmpInst *inst, map_type &variables) {
                // omitted
                if (p == llvm::CmpInst::ICMP_EQ || p ==llvm::CmpInst::ICMP_NE) {
                // omitted
                } else {
                    bool res;
                    switch (p) {
                    case llvm::CmpInst::ICMP_SGE:
                    case llvm::CmpInst::ICMP_UGE:{
                        res = (var_value(assignmnt, lhs) >= var_value(assignmnt, rhs));
                        break;
                    }

// rest omitted....

For a fast fix, we check whether one of the operands (or both) is negative, then change the final result depending on that, as seen here:

            template<typename map_type>
            void handle_scalar_cmp(const llvm::ICmpInst *inst, map_type &variables) {
                // omitted

                if (p == llvm::CmpInst::ICMP_EQ || p ==llvm::CmpInst::ICMP_NE) {
                // omitted
                } else {
                    auto P_HALF = BlueprintFieldType::modulus / 2;
                    bool leftNegative = var_value(assignmnt, lhs) > P_HALF;
                    bool rightNegative = var_value(assignmnt, rhs) > P_HALF;
                    bool res;
                    switch (p) {
                    case llvm::CmpInst::ICMP_SGE:
                    case llvm::CmpInst::ICMP_UGE:{
                        res = (var_value(assignmnt, lhs) >= var_value(assignmnt, rhs));
                        res ^= leftNegative;
                        res ^= rightNegative;
                        break;
                    }
// rest omitted....

Of course, more is needed to solve the soundness, and it requires better handling.

Here is a small example where you can reproduce the problem. The input should be 5. We compare if $(5 * -1) &lt; 10000$, which should be clearly true. Compiled with clang++, the compiled binary writes 1 to the console. When running with the assigner binary and circuit output enabled, we can see that it, in fact, outputs 0.

#include <stdlib.h>
#include <stdint.h>

#ifndef __ZKLLVM__
#include <iostream>
#endif


[[circuit]] int check_negative_number(signed a) {
  #ifdef __ZKLLVM__
  __builtin_assigner_exit_check(a==5);
  #endif
  bool b = (a * -1) < 10000;
  #ifndef __ZKLLVM__
  std::cout << b << std::endl;
  #endif
  return b;
}

#ifndef __ZKLLVM__

int main (int argc, char *argv[]){

    check_negative_number(5);
    return 0;
}
#endif
@nkaskov nkaskov added the bug Something isn't working label Dec 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants