Alias Blocking in Ghidra's Decompiler
Edward J. SchwartzComputer Security Researcher23 min. read

My colleague Cory Cohen recently asked me to take a look at some surprising behavior in Ghidra's decompiler. He had a decompiled function that looked like this:

/* WARNING: Globals starting with '_' overlap smaller symbols at the same address */

void HI_MPI_SVP_NNIE_LoadModel(model *model_buf,uint *out_buf,ulong param3)

{
  uint64_t uVar1;
  byte bVar2;
  byte bVar3;
  ushort uVar4;
  ushort uVar5;
  short sVar6;
  long lVar7;
  uint32_t error;
  int iVar8;
  int flag;
  ulong uVar9;
  ulong neg_counter;
  byte *pbVar10;
  byte *pbVar11;
  uint uVar12;
  ulong uVar13;
  uint64_t vir_addr;
  long lVar14;
  uint uVar15;
  ulong uVar16;
  byte *local_e8;
  uint local_d8;
  uint local_d4;
  header header_buf;
  ulong counter;
  uint size;
  
  lVar7 = ___stack_chk_guard;
  _header_buf = 0;
  error = svp_nnie_check_loadmodel_param_user(model_buf,(long)out_buf);
  if (error == 0) {
    size = (uint)model_buf->size;
    if (size < 0xc1) {
      error = 0xa0338003;
      fprintf(_stderr,
              "[Func]:%s [Line]:%d [Info]:Error(%#x): model_buf->size(%u) must be greater than %u!\n "
              ,"hi_mpi_svp_nnie_load_model",0x345,0xa0338003,(ulong)size,0xc0);
    }
    else {
      vir_addr = model_buf->vir_addr;
      memcpy_s(&header_buf,0xc0,vir_addr);
      if (false) {        fprintf(_stderr,
                "[Func]:%s [Line]:%d [Info]:Error(%#x): model_buf->size(%d) is less than %d!\n",
                "svp_nnie_parse_model_header",0x1b2,0xa0338003);
      }
      else {
        if (false) {
          flag = -1;
        }
        else {
          neg_counter = 0;
          pbVar10 = (byte *)(vir_addr + 4);
          do {
            pbVar11 = pbVar10 + 1;
            size = *(uint *)(&DAT_00105d70 + ((neg_counter ^ *pbVar10) & 0xff) * 4) ^
                   (uint)neg_counter >> 8;
            neg_counter = (ulong)size;
            pbVar10 = pbVar11;
          } while ((byte *)(vir_addr + 0x100000000) != pbVar11);
          flag = ~size;
        }
        if (header_buf.unk1 == flag) {
          memset_s(out_buf,0x36a8,0);
          if (true) {
            fprintf(_stderr,
                    "[Func]:%s [Line]:%d [Info]:Error(%#x):model_version(%u) of input model should b e %d!\n"
                    ,"svp_nnie_parse_model_header",0x1c3,0xa0338003,0,0xb);
          }
          else if (true) {
            fprintf(_stderr,
                    "[Func]:%s [Line]:%d [Info]:Error(%#x):arch_version(%u) of input model should be  %d!\n"
                    ,"svp_nnie_parse_model_header",0x1c6,0xa0338003);
          }
          else {
            *out_buf = 0;
            if (false) {
              fprintf(_stderr,
                      "[Func]:%s [Line]:%d [Info]:Error(%#x): the run_mode(%d) of input model should  be %d!\n"
                      ,"svp_nnie_parse_model_header",0x1d3,0xa0338003,0,0);
            }
            else {
              out_buf[2] = 0;
              if (true) {
                fprintf(_stderr,
                        "[Func]:%s [Line]:%d [Info]:Error(%#x): the net_seg_num(%u) of input model s hould be (0,%d]!\n"
                        ,"svp_nnie_parse_model_header",0x1d7,0xa0338003,0,8);
              }
              else {
                out_buf[1] = 0;
                if (true) {
                  fprintf(_stderr,
                          "[Func]:%s [Line]:%d [Info]:Error(%#x): the u32TemBufSize(%u) of input mod el can\'t be 0!\n"
                          ,"svp_nnie_parse_model_header",0x1db,0xa0338003,0);
                }
                else {
                  if (true) {
                    pbVar10 = (byte *)model_buf->vir_addr;
                    bVar2 = *pbVar10;
                    size = *(uint *)(pbVar10 + 8);
                    out_buf[3] = (uint)bVar2;
                    bVar3 = pbVar10[1];
                    neg_counter = (ulong)bVar3;
                    *(ushort *)(out_buf + 4) = (ushort)bVar3;
                    *(ushort *)((long)out_buf + 0x12) = (ushort)pbVar10[2];
                    *(ushort *)(out_buf + 5) = (ushort)pbVar10[3];
                    uVar4 = *(ushort *)(pbVar10 + 4);
                    out_buf[6] = size;
                    size = *(uint *)(pbVar10 + 0xc);
                    *(ushort *)((long)out_buf + 0x16) = uVar4;
                    out_buf[7] = size;
                    if (bVar2 < 3) {
                      local_d8 = 0;
                      local_d4 = 0;
                      uVar15 = 0;
                      local_e8 = pbVar10;
                      if (bVar3 < 0x11) {
                        do {
                          counter = (ulong)local_d4;
                          lVar14 = counter * 0x69c;
                          uVar5 = *(ushort *)((long)out_buf + lVar14 + 0x12);
                          uVar9 = (ulong)uVar5;
                          if (0x10 < uVar5) {
                            fprintf(_stderr,
                                    "[Func]:%s [Line]:%d [Info]:Error(%#x): model->seg[%d].dst_num(% u) can\'t be greater than %u!\n"
                                    ,"svp_nnie_parse_seg_header",0x20d,0xa0338003,(ulong)local_d4,
                                    uVar9,0x10);
                            goto LAB_001024b8;
                          }
                          if (4 < (ushort)out_buf[counter * 0x1a7 + 5]) {
                            fprintf(_stderr,
                                    "[Func]:%s [Line]:%d [Info]:Error(%#x): model->seg[%d].roi_pool_ num(%u) can\'t be greater than %u!\n"
                                    ,"svp_nnie_parse_seg_header",0x212,0xa0338003,(ulong)local_d4,
                                    (ulong)(ushort)out_buf[counter * 0x1a7 + 5],4);
                            goto LAB_001024b8;
                          }
                          if (0x400 < uVar4) {
                            fprintf(_stderr,
                                    "[Func]:%s [Line]:%d [Info]:Error(%#x): model->seg[%d].max_step( %u) can\'t be greater than %u!\n"
                                    ,"svp_nnie_parse_seg_header",0x216,0xa0338003,(ulong)local_d4,
                                    (ulong)(uint)uVar4,0x400);
                            goto LAB_001024b8;
                          }
                          uVar13 = (ulong)out_buf[counter * 0x1a7 + 6];
                          if (out_buf[counter * 0x1a7 + 6] != 0) {
                            fprintf(_stderr,
                                    "[Func]:%s [Line]:%d [Info]:Error(%#x): model->seg[%d].inst_offs et(%u) can\'t be greater than %u!\n"
                                    ,"svp_nnie_parse_seg_header",0x21c,0xa0338003,(ulong)local_d4,
                                    uVar13,0);
                            goto LAB_001024b8;
                          }
                          if (uVar13 + size != 0) {
                            fprintf(_stderr,
                                    "[Func]:%s [Line]:%d [Info]:Error(%#x): model->seg[%d].inst_offs et(%u)+model->seg[%d].inst_len(%u) can\'t be greater than %u!\n"
                                    ,"svp_nnie_parse_seg_header",0x222,0xa0338003,(ulong)local_d4,
                                    uVar13,(ulong)local_d4,size,0);
                            goto LAB_001024b8;
                          }
                          if ((int)neg_counter != 0) {
                            uVar9 = (ulong)local_d4;
                            out_buf[uVar9 * 0x1a7 + 10] = *(uint *)(local_e8 + 0x10);
                            out_buf[uVar9 * 0x1a7 + 9] = *(uint *)(local_e8 + 0x14);
                            out_buf[uVar9 * 0x1a7 + 0xb] = *(uint *)(local_e8 + 0x18);
                            uVar4 = *(ushort *)(local_e8 + 0x1e);
                            neg_counter = (ulong)uVar4;
                            if ((uVar4 - 2 & 0xfffd) == 0 || 5 < uVar4) {
LAB_00102388:
                              fprintf(_stderr,
                                      "[Func]:%s [Line]:%d [Info]:Error(%#x), the image type(%d) is not supported, image type can\'t be {%d, %d, %d}!\n"
                                      ,"svp_nnie_parse_src_node_info",0x248,0xa0338003,neg_counter,2
                                      ,4,6);
                              error = 0xa0338003;
                              fprintf(_stderr,
                                      "[Func]:%s [Line]:%d [Info]:Error(%#x): parse %d-th seg src no de failed!\n"
                                      ,"hi_mpi_svp_nnie_load_model",0x358,0xa0338003,(ulong)local_d4
                                     );
                              goto joined_r0x001020c4;
                            }
                            size = 0;
                            uVar13 = 0;
                            pbVar11 = local_e8 + 0x20;
                            while( true ) {
                              uVar12 = (uint)neg_counter;
                              if (uVar12 == 3) {
                                out_buf[uVar13 * 0xd + uVar9 * 0x1a7 + 8] = 2;
                                sVar6 = *(short *)(pbVar11 + -4);
                              }
                              else {
                                if (uVar12 == 5) {
                                  out_buf[uVar13 * 0xd + uVar9 * 0x1a7 + 8] = 3;
                                }
                                else {
                                  out_buf[uVar13 * 0xd + uVar9 * 0x1a7 + 8] = uVar12;
                                }
                                sVar6 = *(short *)(pbVar11 + -4);
                              }
                              if (sVar6 == 1) {
                                uVar12 = *(uint *)(pbVar11 + -8);
                                out_buf[uVar13 * 0xd + uVar9 * 0x1a7 + 8] = 4;
                                out_buf[uVar13 * 0xd + uVar9 * 0x1a7 + 9] = uVar12;
                                out_buf[uVar13 * 0xd + uVar9 * 0x1a7 + 0xb] =
                                     *(uint *)(pbVar11 + -0xc);
                              }
                              if (size == 0 && out_buf[counter * 0x1a7 + 3] == 2) {
                                uVar12 = *(uint *)(pbVar11 + -8);
                                out_buf[uVar9 * 0x1a7 + 8] = 5;
                                out_buf[uVar9 * 0x1a7 + 9] = uVar12;
                              }
                              neg_counter = (ulong)size;
                              size = size + 1;
                              strncpy_s(out_buf + neg_counter * 0xd + counter * 0x1a7 + 0xd,0x20,
                                        pbVar11,0x1f);
                              *(undefined1 *)((long)out_buf + uVar9 * 0x69c + uVar13 * 0x34 + 0x53)
                                   = 0;
                              neg_counter = (ulong)(ushort)out_buf[counter * 0x1a7 + 4];
                              if ((ushort)out_buf[counter * 0x1a7 + 4] <= size) break;
                              uVar13 = (ulong)size;
                              out_buf[uVar13 * 0xd + uVar9 * 0x1a7 + 10] = *(uint *)(pbVar11 + 0x30)
                              ;
                              out_buf[uVar13 * 0xd + uVar9 * 0x1a7 + 9] = *(uint *)(pbVar11 + 0x34);
                              out_buf[uVar13 * 0xd + uVar9 * 0x1a7 + 0xb] =
                                   *(uint *)(pbVar11 + 0x38);
                              uVar4 = *(ushort *)(pbVar11 + 0x3e);
                              neg_counter = (ulong)uVar4;
                              pbVar11 = pbVar11 + 0x40;
                              if ((uVar4 - 2 & 0xfffd) == 0 || 5 < uVar4) goto LAB_00102388;
                            }
                            uVar9 = (ulong)*(ushort *)((long)out_buf + lVar14 + 0x12);
                          }
                          if ((int)uVar9 != 0) {
                            if (uVar15 < 0xffffffef) {
                              uVar13 = (ulong)local_d4;
                              pbVar11 = local_e8 + neg_counter * 0x40 + 0x20;
                              uVar9 = 0;
LAB_00101f54:
                              while (pbVar10[(ulong)uVar15 + 0x11] != 1) {
                                uVar15 = uVar15 + 0x30;
                                if (uVar15 != 0) {
                                  fprintf(_stderr,
                                          "[Func]:%s [Line]:%d [Info]:Error(%#x), layer_info_offset( %d) is greater than layer_info_len(%d)!\n"
                                          ,"svp_nnie_parse_dst_node_id",0x28e,0xa0338003,
                                          (ulong)uVar15,0);
                                  goto LAB_00102050;
                                }
                              }
                              uVar16 = (ulong)uVar15;
                              uVar15 = uVar15 + 0x30;
                              out_buf[uVar9 * 0xd + uVar13 * 0x1a7 + 0xdc] =
                                   (uint)*(ushort *)(pbVar10 + uVar16 + 0x1a);
                              if (out_buf[counter * 0x1a7 + 3] == 2) {
                                if (*(short *)(pbVar11 + -4) != 1) {
                                  fprintf(_stderr,
                                          "[Func]:%s [Line]:%d [Info]:Error(%#x), if net type is %d,  the %d-th report data should be tensor!\n"
                                          ,"svp_nnie_parse_dst_node_info",700,0,
                                          (ulong)out_buf[counter * 0x1a7 + 3],uVar9);
                                  uVar9 = (ulong)*(ushort *)((long)out_buf + lVar14 + 0x12);
                                  sVar6 = (short)out_buf[counter * 0x1a7 + 5];
                                  goto joined_r0x0010243c;
                                }
                                if (*(short *)(pbVar11 + -2) == 1) {
                                  out_buf[uVar9 * 0xd + uVar13 * 0x1a7 + 0xd9] =
                                       *(uint *)(pbVar11 + -0x10);
                                  out_buf[uVar9 * 0xd + uVar13 * 0x1a7 + 0xd8] = 5;
                                }
                                else {
                                  out_buf[uVar9 * 0xd + uVar13 * 0x1a7 + 0xda] = 1;
                                  out_buf[uVar9 * 0xd + uVar13 * 0x1a7 + 0xdb] = 1;
                                  out_buf[uVar9 * 0xd + uVar13 * 0x1a7 + 0xd8] = 4;
                                  out_buf[uVar9 * 0xd + uVar13 * 0x1a7 + 0xd9] =
                                       *(uint *)(pbVar11 + -0x10);
                                }
                              }
                              else {
                                out_buf[uVar9 * 0xd + uVar13 * 0x1a7 + 0xda] =
                                     *(uint *)(pbVar11 + -0x10);
                                out_buf[uVar9 * 0xd + uVar13 * 0x1a7 + 0xd9] =
                                     *(uint *)(pbVar11 + -0xc);
                                out_buf[uVar9 * 0xd + uVar13 * 0x1a7 + 0xdb] =
                                     *(uint *)(pbVar11 + -8);
                                if (*(short *)(pbVar11 + -4) == 0) {
                                  out_buf[uVar9 * 0xd + uVar13 * 0x1a7 + 0xdb] =
                                       *(uint *)(pbVar11 + -0xc);
                                  size = *(uint *)(pbVar11 + -8);
                                  out_buf[uVar9 * 0xd + uVar13 * 0x1a7 + 0xd8] = 4;
                                  out_buf[uVar9 * 0xd + uVar13 * 0x1a7 + 0xd9] = size;
                                }
                                else {
                                  if (*(short *)(pbVar11 + -4) != 1) {
                                    fprintf(_stderr,
                                            "[Func]:%s [Line]:%d [Info]:Error(%#x), report node mode  shoule be 0 or 1!\n"
                                            ,"svp_nnie_parse_dst_node_info",0x2d6,0);
                                    uVar9 = (ulong)*(ushort *)((long)out_buf + lVar14 + 0x12);
                                    goto LAB_0010218c;
                                  }
                                  out_buf[uVar9 * 0xd + uVar13 * 0x1a7 + 0xd8] = 0;
                                }
                              }
                              size = (int)uVar9 + 1;
                              uVar16 = (ulong)size;
                              strncpy_s(out_buf + uVar9 * 0xd + counter * 0x1a7 + 0xdd,0x20,pbVar11,
                                        0x1f);
                              *(undefined1 *)((long)out_buf + uVar13 * 0x69c + uVar9 * 0x34 + 0x393)
                                   = 0;
                              uVar4 = *(ushort *)((long)out_buf + lVar14 + 0x12);
                              uVar9 = (ulong)uVar4;
                              if (uVar4 <= size) goto LAB_0010218c;
                              pbVar11 = pbVar11 + 0x40;
                              uVar9 = uVar16;
                              if (0xffffffee < uVar15) goto LAB_0010201c;
                              goto LAB_00101f54;
                            }
                            uVar16 = 0;
LAB_0010201c:
                            fprintf(_stderr,
                                    "[Func]:%s [Line]:%d [Info]:Error(%#x), layer_info_offset(%d) sh ould be less than %d!\n"
                                    ,"svp_nnie_parse_dst_node_id",0x288,0xa0338003,(ulong)uVar15,
                                    0xffffffef);
                            uVar9 = uVar16;
LAB_00102050:
                            fprintf(_stderr,
                                    "[Func]:%s [Line]:%d [Info]:Error(%#x), %d-th node, svp_nnie_par se_dst_node_id failed!\n"
                                    ,"svp_nnie_parse_dst_node_info",0x2b5,0xa0338003,uVar9);
                            error = 0xa0338003;
                            fprintf(_stderr,
                                    "[Func]:%s [Line]:%d [Info]:Error(%#x): parse %d-th seg dst node  failed!\n"
                                    ,"hi_mpi_svp_nnie_load_model",0x360,0xa0338003,(ulong)local_d4);
                            goto joined_r0x001020c4;
                          }
LAB_0010218c:
                          sVar6 = (short)out_buf[counter * 0x1a7 + 5];
joined_r0x0010243c:
                          lVar14 = uVar9 * 0x40 + neg_counter * 0x40 + 0x10;
                          if (sVar6 == 0) {
                            uVar4 = 0;
                          }
                          else {
                            neg_counter = 0;
                            pbVar11 = local_e8 + lVar14 + 0x40;
                            size = local_d8;
                            do {
                              out_buf[(ulong)local_d4 * 0x1a7 + neg_counter + 0x1a8] = size;
                              uVar12 = (int)neg_counter + 1;
                              neg_counter = (ulong)uVar12;
                              local_d8 = size + 1;
                              out_buf[(ulong)size * 0x1a + 0xd48] = *(uint *)(pbVar11 + -0x40);
                              out_buf[(ulong)size * 0x1a + 0xd3b] = *(uint *)(pbVar11 + -0x3c);
                              out_buf[(ulong)size * 0x1a + 0xd3c] = *(uint *)(pbVar11 + -0x38);
                              out_buf[(ulong)size * 0x1a + 0xd47] = *(uint *)(pbVar11 + -0x34);
                              out_buf[(ulong)size * 0x1a + 0xd3e] = *(uint *)(pbVar11 + -0x30);
                              out_buf[(ulong)size * 0x1a + 0xd3f] = *(uint *)(pbVar11 + -0x2c);
                              out_buf[(ulong)size * 0x1a + 0xd3d] = *(uint *)(pbVar11 + -0x28);
                              out_buf[(ulong)size * 0x1a + 0xd40] = *(uint *)(pbVar11 + -0x24);
                              out_buf[(ulong)size * 0x1a + 0xd46] = *(uint *)(pbVar11 + -0x20);
                              out_buf[(ulong)size * 0x1a + 0xd45] = *(uint *)(pbVar11 + -0x1c);
                              out_buf[(ulong)size * 0x1a + 0xd41] = *(uint *)(pbVar11 + -0x18);
                              out_buf[(ulong)size * 0x1a + 0xd42] = *(uint *)(pbVar11 + -0x14);
                              out_buf[(ulong)size * 0x1a + 0xd43] = *(uint *)(pbVar11 + -0x10);
                              out_buf[(ulong)size * 0x1a + 0xd44] = *(uint *)(pbVar11 + -0xc);
                              out_buf[(ulong)size * 0x1a + 0xd49] = (uint)pbVar11[-8];
                              out_buf[(ulong)size * 0x1a + 0xd4b] = (uint)pbVar11[-7];
                              out_buf[(ulong)size * 0x1a + 0xd4c] = (uint)pbVar11[-6];
                              out_buf[(ulong)size * 0x1a + 0xd4a] = (uint)pbVar11[-5];
                              strncpy_s(out_buf + (ulong)size * 0x1a + 0xd4d,0x20,pbVar11,0x1f);
                              *(undefined1 *)((long)out_buf + (ulong)size * 0x68 + 0x3553) = 0;
                              uVar4 = (ushort)out_buf[counter * 0x1a7 + 5];
                              pbVar11 = pbVar11 + 0x70;
                              size = local_d8;
                            } while (uVar12 < uVar4);
                          }
                          local_d4 = local_d4 + 1;
                          local_e8 = local_e8 + (int)((uint)uVar4 * 0x70) + lVar14;
                          if (out_buf[2] <= local_d4) {
                            error = 0;
                            vir_addr = model_buf->phy_addr;
                            uVar1 = model_buf->vir_addr;
                            out_buf[0xda8] = 0;
                            *(uint64_t *)(out_buf + 0xda4) = vir_addr;
                            *(uint64_t *)(out_buf + 0xda6) = uVar1;
                            iVar8 = svp_nnie_check_model_user(out_buf);
                            if (iVar8 != 0) {
                              error = 0xa0338003;
                              fprintf(_stderr,
                                      "[Func]:%s [Line]:%d [Info]:Error(%#x), SVP_NNIE_CheckModelUse r failed!\n"
                                      ,"hi_mpi_svp_nnie_load_model",0x371,0xa0338003);
                            }
                            goto joined_r0x001020c4;
                          }
                          bVar2 = *local_e8;
                          out_buf[(ulong)local_d4 * 0x1a7 + 3] = (uint)bVar2;
                          *(ushort *)(out_buf + (ulong)local_d4 * 0x1a7 + 4) = (ushort)local_e8[1];
                          *(ushort *)((long)out_buf + (ulong)local_d4 * 0x69c + 0x12) =
                               (ushort)local_e8[2];
                          *(ushort *)(out_buf + (ulong)local_d4 * 0x1a7 + 5) = (ushort)local_e8[3];
                          uVar4 = *(ushort *)(local_e8 + 4);
                          *(ushort *)((long)out_buf + (ulong)local_d4 * 0x69c + 0x16) = uVar4;
                          out_buf[(ulong)local_d4 * 0x1a7 + 6] = *(uint *)(local_e8 + 8);
                          size = *(uint *)(local_e8 + 0xc);
                          out_buf[(ulong)local_d4 * 0x1a7 + 7] = size;
                          if (2 < bVar2) goto LAB_0010247c;
                          neg_counter = (ulong)(ushort)out_buf[(ulong)local_d4 * 0x1a7 + 4];
                        } while ((ushort)out_buf[(ulong)local_d4 * 0x1a7 + 4] < 0x11);
                      }
                      fprintf(_stderr,
                              "[Func]:%s [Line]:%d [Info]:Error(%#x): model->seg[%d].src_num(%u) can \'t be greater than %u!\n"
                              ,"svp_nnie_parse_seg_header",0x209,0xa0338003,(ulong)local_d4,
                              neg_counter,0x10);
                    }
                    else {
                      local_d4 = 0;
LAB_0010247c:
                      fprintf(_stderr,
                              "[Func]:%s [Line]:%d [Info]:Error(%#x): model->seg[%d].net_type(%d) sh ould be [%d,%d)!\n"
                              ,"svp_nnie_parse_seg_header",0x205,0xa0338003,(ulong)local_d4,
                              (ulong)bVar2,0,3);
                    }
LAB_001024b8:
                    error = 0xa0338003;
                    fprintf(_stderr,
                            "[Func]:%s [Line]:%d [Info]:Error(%#x): parse %d-th seg head info failed !\n"
                            ,"hi_mpi_svp_nnie_load_model",0x352,0xa0338003,(ulong)local_d4);
                    goto joined_r0x001020c4;
                  }
                  fprintf(_stderr,
                          "[Func]:%s [Line]:%d [Info]:Error(%#x): model_buf->size(%u) can\'t be less  than %llu!\n"
                          ,"svp_nnie_parse_model_header",0x1e0,0xa0338003);
                }
              }
            }
          }
        }
        else {
          fprintf(_stderr,"[Func]:%s [Line]:%d [Info]:Error(%#x): check file failed!\n",
                  "svp_nnie_parse_model_header",0x1b5,0xa0338003);
        }
      }
      error = 0xa0338003;
      fprintf(_stderr,"[Func]:%s [Line]:%d [Info]:Error(%#x): check model head info failed!\n",
              "hi_mpi_svp_nnie_load_model",0x347,0xa0338003);
    }
  }
  else {
    fprintf(_stderr,"[Func]:%s [Line]:%d [Info]:Error(%#x): check model input param failed!\n",
            "hi_mpi_svp_nnie_load_model",0x340,(ulong)error);
  }
joined_r0x001020c4:
  if (lVar7 == ___stack_chk_guard) {
    return;
  }
                    /* WARNING: Subroutine does not return */
  __stack_chk_fail(error);
}

Cory wanted to know why the decompiler had simplified certain branch conditions to true or false, such as the one on line 47. Seeing true or false branch conditions is somewhat rare in decompilers since compilers often perform these types of optimizations. My first issue was that my decompilation output looked much shorter than his:

/* WARNING: Removing unreachable block (ram,0x00101c60) */
/* WARNING: Removing unreachable block (ram,0x001026f8) */
/* WARNING: Removing unreachable block (ram,0x00101c6c) */
/* WARNING: Removing unreachable block (ram,0x0010272c) */
/* WARNING: Removing unreachable block (ram,0x00101c58) */
/* WARNING: Removing unreachable block (ram,0x001026c8) */
/* WARNING: Removing unreachable block (ram,0x00101c8c) */
/* WARNING: Removing unreachable block (ram,0x00101cb0) */
/* WARNING: Removing unreachable block (ram,0x00102478) */
/* WARNING: Removing unreachable block (ram,0x00101d0c) */
/* WARNING: Removing unreachable block (ram,0x00101d1c) */
/* WARNING: Removing unreachable block (ram,0x0010264c) */
/* WARNING: Removing unreachable block (ram,0x00101d38) */
/* WARNING: Removing unreachable block (ram,0x00102614) */
/* WARNING: Removing unreachable block (ram,0x00101d44) */
/* WARNING: Removing unreachable block (ram,0x001025d8) */
/* WARNING: Removing unreachable block (ram,0x00101d4c) */
/* WARNING: Removing unreachable block (ram,0x001025a0) */
/* WARNING: Removing unreachable block (ram,0x00101d5c) */
/* WARNING: Removing unreachable block (ram,0x00102560) */
/* WARNING: Removing unreachable block (ram,0x00101d6c) */
/* WARNING: Removing unreachable block (ram,0x00101d70) */
/* WARNING: Removing unreachable block (ram,0x00101db0) */
/* WARNING: Removing unreachable block (ram,0x00101db4) */
/* WARNING: Removing unreachable block (ram,0x00101db8) */
/* WARNING: Removing unreachable block (ram,0x00101e9c) */
/* WARNING: Removing unreachable block (ram,0x00101dd8) */
/* WARNING: Removing unreachable block (ram,0x00101ef0) */
/* WARNING: Removing unreachable block (ram,0x00101de0) */
/* WARNING: Removing unreachable block (ram,0x00101de4) */
/* WARNING: Removing unreachable block (ram,0x00101eb4) */
/* WARNING: Removing unreachable block (ram,0x00101ec8) */
/* WARNING: Removing unreachable block (ram,0x00101df0) */
/* WARNING: Removing unreachable block (ram,0x00101df8) */
/* WARNING: Removing unreachable block (ram,0x00101dfc) */
/* WARNING: Removing unreachable block (ram,0x00101e00) */
/* WARNING: Removing unreachable block (ram,0x00101e10) */
/* WARNING: Removing unreachable block (ram,0x00101efc) */
/* WARNING: Removing unreachable block (ram,0x00101f00) */
/* WARNING: Removing unreachable block (ram,0x00101f1c) */
/* WARNING: Removing unreachable block (ram,0x00101f2c) */
/* WARNING: Removing unreachable block (ram,0x00101f54) */
/* WARNING: Removing unreachable block (ram,0x00101f48) */
/* WARNING: Removing unreachable block (ram,0x001020d0) */
/* WARNING: Removing unreachable block (ram,0x00101f64) */
/* WARNING: Removing unreachable block (ram,0x00102118) */
/* WARNING: Removing unreachable block (ram,0x001023f8) */
/* WARNING: Removing unreachable block (ram,0x00102124) */
/* WARNING: Removing unreachable block (ram,0x0010214c) */
/* WARNING: Removing unreachable block (ram,0x00102130) */
/* WARNING: Removing unreachable block (ram,0x00101f90) */
/* WARNING: Removing unreachable block (ram,0x00102108) */
/* WARNING: Removing unreachable block (ram,0x00102160) */
/* WARNING: Removing unreachable block (ram,0x00102110) */
/* WARNING: Removing unreachable block (ram,0x00101fb0) */
/* WARNING: Removing unreachable block (ram,0x00101fc8) */
/* WARNING: Removing unreachable block (ram,0x0010200c) */
/* WARNING: Removing unreachable block (ram,0x00102558) */
/* WARNING: Removing unreachable block (ram,0x0010201c) */
/* WARNING: Removing unreachable block (ram,0x00102050) */
/* WARNING: Removing unreachable block (ram,0x0010218c) */
/* WARNING: Removing unreachable block (ram,0x00102440) */
/* WARNING: Removing unreachable block (ram,0x001021a4) */
/* WARNING: Removing unreachable block (ram,0x001021bc) */
/* WARNING: Removing unreachable block (ram,0x001022a8) */
/* WARNING: Removing unreachable block (ram,0x001024f0) */
/* WARNING: Removing unreachable block (ram,0x00102520) */
/* WARNING: Removing unreachable block (ram,0x001022d8) */
/* WARNING: Removing unreachable block (ram,0x0010247c) */
/* WARNING: Removing unreachable block (ram,0x00102338) */
/* WARNING: Removing unreachable block (ram,0x00101e50) */
/* WARNING: Removing unreachable block (ram,0x00101e94) */
/* WARNING: Removing unreachable block (ram,0x00101e98) */
/* WARNING: Removing unreachable block (ram,0x00102388) */
/* WARNING: Removing unreachable block (ram,0x00102348) */
/* WARNING: Removing unreachable block (ram,0x001024b8) */
/* WARNING: Removing unreachable block (ram,0x00102800) */
/* WARNING: Removing unreachable block (ram,0x00101c80) */
/* WARNING: Removing unreachable block (ram,0x001027cc) */
/* WARNING: Removing unreachable block (ram,0x00102688) */
/* WARNING: Removing unreachable block (ram,0x0010279c) */
/* WARNING: Globals starting with '_' overlap smaller symbols at the same address */

void HI_MPI_SVP_NNIE_LoadModel(model *model_buf,uint *out_buf,ulong param3)

{
  long lVar1;
  uint32_t error;
  int flag;
  ulong neg_counter;
  byte *pbVar2;
  uint64_t vir_addr;
  header header_buf;
  byte *pbVar3;
  uint size;
  
  lVar1 = ___stack_chk_guard;
  _header_buf = 0;
  error = svp_nnie_check_loadmodel_param_user(model_buf,(long)out_buf);
  if (error == 0) {
    size = (uint)model_buf->size;
    if (size < 0xc1) {
      error = 0xa0338003;
      fprintf(_stderr,
              "[Func]:%s [Line]:%d [Info]:Error(%#x): model_buf->size(%u) must be greater than %u!\n "
              ,"hi_mpi_svp_nnie_load_model",0x345,0xa0338003,(ulong)size,0xc0);
    }
    else {
      vir_addr = model_buf->vir_addr;
      memcpy_s(&header_buf,0xc0,vir_addr);
      neg_counter = 0;      pbVar3 = (byte *)(vir_addr + 4);
      do {
        pbVar2 = pbVar3 + 1;
        size = *(uint *)(&DAT_00105d70 + ((neg_counter ^ *pbVar3) & 0xff) * 4) ^
               (uint)neg_counter >> 8;
        neg_counter = (ulong)size;
        pbVar3 = pbVar2;
      } while ((byte *)(vir_addr + 0x100000000) != pbVar2);
      if (header_buf.unk1 == ~size) {
        memset_s(out_buf,0x36a8,0);
        fprintf(_stderr,
                "[Func]:%s [Line]:%d [Info]:Error(%#x):model_version(%u) of input model should be %d !\n"
                ,"svp_nnie_parse_model_header",0x1c3,0xa0338003,0,0xb);
      }
      else {
        fprintf(_stderr,"[Func]:%s [Line]:%d [Info]:Error(%#x): check file failed!\n",
                "svp_nnie_parse_model_header",0x1b5,0xa0338003);
      }
      error = 0xa0338003;
      fprintf(_stderr,"[Func]:%s [Line]:%d [Info]:Error(%#x): check model head info failed!\n",
              "hi_mpi_svp_nnie_load_model",0x347,0xa0338003);
    }
  }
  else {
    fprintf(_stderr,"[Func]:%s [Line]:%d [Info]:Error(%#x): check model input param failed!\n",
            "hi_mpi_svp_nnie_load_model",0x340,(ulong)error);
  }
  if (lVar1 == ___stack_chk_guard) {
    return;
  }
                    /* WARNING: Subroutine does not return */
  __stack_chk_fail(error);
}

We figured out that I had the "Eliminate unreachable code" option enabled under Ghidra's Options => Decompiler => Analysis options tab, but he did not.

Ghidra Decompiler Analysis options
Ghidra Decompiler Analysis options
Unsurprisingly, Ghidra decided that the body of an if (false) { ... } statement was unreachable code and removed it.

With that small mystery solved, I obtained the same decompilation as Cory, but we still didn't know why Ghidra had simplified away the condition. This task was made a lot more challenging since it was an ARM executable, and neither of us are very familiar with ARM assembly code. Cory eventually figured out that on the same decompiler settings tab, changing the "Alias Blocking" option from "Arrays and Structures" to "None" caused Ghidra to preserve the original condition.

Ghidra's documentation on Alias Blocking explains:

When deciding if an individual stack location has become dead, the Decompiler must consider aliases, pointers onto the stack that could be used to modify the location within a called function. One strong heuristic the Decompiler uses is: if the user has explicitly created a variable on the stack between the base location referenced by the pointer and the individual stack location, then the Decompiler can assume that the pointer is not an alias of the stack location. The alias is blocked by the explicit variable. However, if the user's explicit variable is labeling something that isn't really an explicit variable, like a field within a larger structure, for instance, the Decompiler may incorrectly consider the stack location as dead and start removing live code.

In order to support the exploratory labeling of stack locations, the user can use this setting to specify what data-types should be considered blocking. The four options are:

  • None - No data-type is considered blocking
  • Structures - Only structures are blocking
  • Structures and Arrays - Only structures and arrays are blocking
  • All Data-types - All data-types are blocking

Selecting None is the equivalent of turning off the heuristic. Selecting anything except All Data-types allows users to safely label small variables without knowing immediately if the stack location is part of a larger structure or array.

To better understand what is going on, here is one of the relevant snippets that went from false to a real condition:

    else {
      vir_addr = model_buf->vir_addr;
      memcpy_s(&header_buf,0xc0,vir_addr);
      size = local_98._4_4_ + (uint)uStack_90;      if ((uint)model_buf->size < size) {        fprintf(_stderr,
                "[Func]:%s [Line]:%d [Info]:Error(%#x): model_buf->size(%d) is less than %d!\n",
                "svp_nnie_parse_model_header",0x1b2,0xa0338003);
      }

Looking at the stack frame, we can start to piece together what happened:

  1. local_98 and uStack_90 were initialized to zero.
  2. memcpy_s was called with a pointer to header_buf, which is a structure of size 0xc0 bytes on the stack.
  3. Since header_buf is a structure, Alias Blocking with "Arrays and Structures" enabled means that Ghidra assumes that local_98 and uStack_90 cannot be modified by the memcpy_s call, since they are not part of the header_buf structure. Therefore, Ghidra concludes that they are unchanged, size is always zero, and the condition is always false.

Unfortunately for Cory, the header_buf structure he created was only four bytes long. In reality, local_98 and uStack_90 are fields that are overwritten by the memcpy_s call, and influence the computation of the condition. But his artificially small structure misled Ghidra into thinking they were unaffected.

So What?

I had never heard of the "Alias Blocking" option before, and I'm not aware of a similar feature in other decompilers. It's an interesting idea though. Basically, the assumption is that if you have enough information to type an array or structure on the stack, then it is accurate enough to assume there won't be pointers from that object that alias outside of that object. There's also a number of other things that have to work correctly for this analysis to be correct. If we had confused the calling convention of memcpy_s and did not detect that it takes a pointer to the stack, then this analysis would also be incorrect.

It's also interesting because it has the potential to both help and hurt decompilation quality, depending on the situation. Cory noted this functionality could be helpful for deobfuscating opaque predicates, for example. But Cory also noted that he often retypes structs incrementally as he learns more about them. Alias Blocking on structures would actually be harmful for this type of incremental typing.

Anti-analysis?

Another interesting question is whether we can abuse this behavior to make it harder to analyze code. To answer this, I created a small test program that can exhibit the same behavior. Here it is (also on Godbolt):

#include <string.h>
#include <stdio.h>

struct foo {
  int blue;
  int red;
  char green;
  float two;
};

void go(struct foo* in) {
    int above = -1;
    struct foo myfoo;
    myfoo.red = 0;
    int below = 2;
    memcpy(&myfoo, in, sizeof(myfoo));
    if (myfoo.red != 10) {
        printf("Wow!\n");
    }
}

int main() {
    struct foo mainfoo = {4, 5, 'a', 3.14};
}

I found that I had to compile with gcc -fno-builtin or otherwise gcc would fairly aggressively optimize away the memcpy call!

Upon initially loading Ghidra, the decompilation of the go function looks like this:

/* WARNING: Unknown calling convention -- yet parameter storage is locked */

long go(void)

{
  void *in_RDI;
  long in_FS_OFFSET;
  undefined1 local_28 [4];
  int local_24;
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  local_24 = 0;
  memcpy(local_28,in_RDI,0x10);
  if (local_24 != 10) {
    printf("Wow!\n");
  }
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;
}

For some reason undefined1[4] is not considered to be an Array type. But if we manually retype local_28 to char[4] instead of undefined1[4], we see the desired behavior:

/* WARNING: Unknown calling convention -- yet parameter storage is locked */

long go(void)

{
  long lVar1;
  void *in_RDI;
  long in_FS_OFFSET;
  char local_28 [4];
  
  lVar1 = *(long *)(in_FS_OFFSET + 0x28);
  memcpy(local_28,in_RDI,0x10);
  if (true) {
    printf("Wow!\n");
  }
  if (lVar1 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;
}

Of course, we could have made that condition appear to be false.

If we could nudge Ghidra to type local_28 as a char[4] array automatically, or a structure, perhaps we could automatically cause Ghidra to misinterpret the condition. The alias blocking documentation suggests that it only applies when a user "explicitly" creates a variable on the stack, so this might prevent abuse for anti-analysis. I leave this as an open question for the reader.

Powered with by Gatsby 5.0