enum type_cmp type_cmp(type *a, type *b, enum type_cmp_opts opts) { const enum type_cmp cmp = type_cmp_r(a, b, opts); if(cmp == TYPE_CONVERTIBLE_EXPLICIT){ /* try for implicit void * conversion */ if(type_is_void_ptr(a) && type_is_ptr(b)) return TYPE_CONVERTIBLE_IMPLICIT; if(type_is_void_ptr(b) && type_is_ptr(a)) return TYPE_CONVERTIBLE_IMPLICIT; } return cmp; }
static void check_arg_voidness_and_nonnulls( expr *callexpr, symtable *stab, funcargs *args_from_decl, unsigned count_decl, expr **exprargs, char *sp) { /* this block folds the args and type-checks */ unsigned long nonnulls = 0; unsigned i; attribute *da; if((da = func_or_builtin_attr_present(callexpr, attr_nonnull))) nonnulls = da->bits.nonnull_args; for(i = 0; exprargs[i]; i++){ expr *arg = FOLD_EXPR(exprargs[i], stab); char buf[64]; ARG_BUF(buf, i, sp); if(fold_check_expr(arg, FOLD_CHK_NO_ST_UN, buf)) continue; if(i < count_decl && (nonnulls & (1 << i)) && type_is_ptr(args_from_decl->arglist[i]->ref) && expr_is_null_ptr(arg, NULL_STRICT_INT)) { cc1_warn_at(&arg->where, attr_nonnull, "null passed where non-null required (arg %d)", i + 1); } } }
static type *is_val_ptr(const out_val *v) { type *pointee = type_is_ptr(v->t); switch(v->type){ case V_REG_SPILT: if(pointee){ type *next = type_is_ptr(pointee); if(next) return next; } return NULL; default: return pointee ? v->t : NULL; } }
void sanitize_boundscheck( expr *elhs, expr *erhs, out_ctx *octx, const out_val *lhs, const out_val *rhs) { decl *array_decl = NULL; type *array_ty; expr *expr_sz; consty sz; const out_val *val; if(!(cc1_sanitize & CC1_UBSAN)) return; if(type_is_ptr(elhs->tree_type)) array_decl = expr_to_declref(elhs, NULL), val = rhs; else if(type_is_ptr(erhs->tree_type)) array_decl = expr_to_declref(erhs, NULL), val = lhs; if(!array_decl) return; if(!(array_ty = type_is(array_decl->ref, type_array))) return; expr_sz = array_ty->bits.array.size; if(!expr_sz) return; const_fold(expr_sz, &sz); if(sz.type != CONST_NUM) return; if(!K_INTEGRAL(sz.bits.num)) return; /* force unsigned compare, which catches negative indexes */ sanitize_assert_order(val, op_le, sz.bits.num.val.i, uintptr_ty(), octx, "bounds"); }
int expr_is_null_ptr(expr *e, enum null_strictness ty) { /* 6.3.2.3: * * An integer constant expression with the value 0, or such an expression * cast to type void *, is called a null pointer constant * * NULL_STRICT_ANY_PTR is used for sentinel checks, * i.e. any null type pointer */ int b = 0; /* void * always qualifies */ if(type_is_primitive(type_is_ptr(e->tree_type), type_void)) b = 1; else if(ty == NULL_STRICT_INT && type_is_integral(e->tree_type)) b = 1; else if(ty == NULL_STRICT_ANY_PTR && type_is_ptr(e->tree_type)) b = 1; return b && const_expr_and_zero(e); }
type *type_dereference_decay(type *const ty_ptr) { type *const pointee = type_is_ptr(ty_ptr); assert(pointee); /* *(void (*)()) does nothing */ if(type_is(pointee, type_func)) return ty_ptr; /* decay never returns an array type - decay to pointer */ if(type_is(pointee, type_array)) return type_decay(pointee); return pointee; }
static int calc_ptr_step(type *t) { type *tnext; /* we are calculating the sizeof *t */ if(type_is_primitive(type_is_ptr(t), type_void)) return type_primitive_size(type_void); if(type_is_primitive(t, type_unknown)) return 1; tnext = type_next(t); if(type_is_vla(tnext, VLA_ANY_DIMENSION)) return -1; return type_size(tnext, NULL); }
enum type_str_type type_str_type(type *r) { type *t = type_is_array(r); if(!t) t = type_is_ptr(r); t = type_is_primitive(t, type_unknown); switch(t ? t->bits.type->primitive : type_unknown){ case type_schar: case type_nchar: case type_uchar: return type_str_char; case type_int: return type_str_wchar; default: return type_str_no; } }
static enum type_cmp type_cmp_r( type *const orig_a, type *const orig_b, enum type_cmp_opts opts) { enum type_cmp ret; type *a, *b; int subchk = 1; if(!orig_a || !orig_b) return orig_a == orig_b ? TYPE_EQUAL : TYPE_NOT_EQUAL; a = type_skip_all(orig_a); b = type_skip_all(orig_b); /* array/func decay takes care of any array->ptr checks */ if(a->type != b->type){ /* allow _Bool <- pointer */ if(type_is_primitive(a, type__Bool) && type_is_ptr(b)) return TYPE_CONVERTIBLE_IMPLICIT; /* allow int <-> ptr (or block) */ if((type_is_ptr_or_block(a) && type_is_integral(b)) || (type_is_ptr_or_block(b) && type_is_integral(a))) { return TYPE_CONVERTIBLE_EXPLICIT; } /* allow void <- anything */ if(type_is_void(a)) return TYPE_CONVERTIBLE_IMPLICIT; /* allow block <-> fnptr */ if((type_is_fptr(a) && type_is(b, type_block)) || (type_is_fptr(b) && type_is(a, type_block))) { return TYPE_CONVERTIBLE_EXPLICIT; } return TYPE_NOT_EQUAL; } switch(a->type){ case type_auto: ICE("__auto_type"); case type_btype: subchk = 0; ret = btype_cmp(a->bits.type, b->bits.type); break; case type_array: if(a->bits.array.is_vla || b->bits.array.is_vla){ /* fine, pretend they're equal even if different expressions */ ret = TYPE_EQUAL_TYPEDEF; }else{ const int a_has_sz = !!a->bits.array.size; const int b_has_sz = !!b->bits.array.size; if(a_has_sz && b_has_sz){ integral_t av = const_fold_val_i(a->bits.array.size); integral_t bv = const_fold_val_i(b->bits.array.size); if(av != bv) return TYPE_NOT_EQUAL; }else if(a_has_sz != b_has_sz){ if((opts & TYPE_CMP_ALLOW_TENATIVE_ARRAY) == 0) return TYPE_NOT_EQUAL; } } /* next */ break; case type_block: case type_ptr: break; case type_cast: case type_tdef: case type_attr: case type_where: ICE("should've been skipped"); case type_func: switch(funcargs_cmp(a->bits.func.args, b->bits.func.args)){ case FUNCARGS_EXACT_EQUAL: case FUNCARGS_IMPLICIT_CONV: break; default: /* "void (int)" and "void (int, int)" aren't equal, * but a cast can soon fix it */ return TYPE_CONVERTIBLE_EXPLICIT; } break; } if(subchk) ret = type_cmp_r(a->ref, b->ref, opts); if(ret == TYPE_NOT_EQUAL && a->type == type_func) { /* "int (int)" and "void (int)" aren't equal - but castable */ ret = TYPE_CONVERTIBLE_EXPLICIT; } if(ret == TYPE_NOT_EQUAL && a->type == type_ptr && fopt_mode & FOPT_PLAN9_EXTENSIONS) { /* allow b to be an anonymous member of a, if pointers */ struct_union_enum_st *a_sue = type_is_s_or_u(a), *b_sue = type_is_s_or_u(b); if(a_sue && b_sue /* already know they aren't equal */){ /* b_sue has an a_sue, * the implicit cast adjusts to return said a_sue */ if(struct_union_member_find_sue(b_sue, a_sue)) return TYPE_CONVERTIBLE_IMPLICIT; } } /* allow ptr <-> ptr */ if(ret == TYPE_NOT_EQUAL && type_is_ptr(a) && type_is_ptr(b)) ret = TYPE_CONVERTIBLE_EXPLICIT; /* char * and int * are explicitly conv., * even though char and int are implicit */ if(ret == TYPE_CONVERTIBLE_IMPLICIT && a->type == type_ptr) ret = TYPE_CONVERTIBLE_EXPLICIT; if(a->type == type_ptr || a->type == type_block){ switch(ret){ #define MAP(a, b) case a: ret = b; break MAP(TYPE_QUAL_ADD, TYPE_QUAL_POINTED_ADD); MAP(TYPE_QUAL_SUB, TYPE_QUAL_POINTED_SUB); MAP(TYPE_QUAL_POINTED_ADD, TYPE_QUAL_NESTED_CHANGE); MAP(TYPE_QUAL_POINTED_SUB, TYPE_QUAL_NESTED_CHANGE); #undef MAP default: break; } } if(ret & TYPE_EQUAL_ANY){ enum type_qualifier a_qual = type_qual(orig_a); enum type_qualifier b_qual = type_qual(orig_b); if(a_qual && b_qual){ switch(type_qual_cmp(a_qual, b_qual)){ case -1: /* a has more */ ret = TYPE_QUAL_ADD; break; case 1: /* b has more */ ret = TYPE_QUAL_SUB; break; } }else if(a_qual){ ret = TYPE_QUAL_ADD; }else if(b_qual){ ret = TYPE_QUAL_SUB; } /* else neither are casts */ } if(ret == TYPE_EQUAL){ int at = orig_a->type == type_tdef; int bt = orig_b->type == type_tdef; if(at != bt){ /* one is a typedef */ ret = TYPE_EQUAL_TYPEDEF; }else if(at){ /* both typedefs */ if(orig_a->bits.tdef.decl != orig_b->bits.tdef.decl){ ret = TYPE_EQUAL_TYPEDEF; } } /* else no typedefs */ } return ret; }