Skip to content

Commit

Permalink
treewide: Avoid offsetting NULL pointers.
Browse files Browse the repository at this point in the history
This is undefined behavior and was reported by UB Sanitizer:
  lib/meta-flow.c:3445:16: runtime error:
    member access within null pointer of type 'struct vl_mf_field'
      0 0x6aad0f in mf_get_vl_mff lib/meta-flow.c:3445
      1 0x6d96d7 in mf_from_oxm_header lib/nx-match.c:260
      2 0x6d9e2e in nx_pull_header__ lib/nx-match.c:341
      3 0x6daafa in nx_pull_header lib/nx-match.c:488
      4 0x6abcb6 in mf_vl_mff_nx_pull_header lib/meta-flow.c:3605
      5 0x73b9be in decode_NXAST_RAW_REG_MOVE lib/ofp-actions.c:2652
      6 0x764ccd in ofpact_decode lib/ofp-actions.inc2:4681
      [...]
  lib/sset.c:315:12: runtime error: applying zero offset to null pointer
      0 0xcc2e6a in sset_at_position lib/sset.c:315:12
      1 0x5734b3 in port_dump_next ofproto/ofproto-dpif.c:4083:20
      [...]
  lib/ovsdb-data.c:2194:56:
  runtime error: applying zero offset to null pointer
      0 0x5e9530 in ovsdb_datum_added_removed lib/ovsdb-data.c:2194:56
      1 0x4d6258 in update_row_ref_count ovsdb/transaction.c:335:17
      2 0x4c360b in for_each_txn_row ovsdb/transaction.c:1572:33
      [...]
  lib/ofpbuf.c:440:30:
  runtime error: applying zero offset to null pointer
      0 0x75066d in ofpbuf_push_uninit lib/ofpbuf.c:440
      1 0x46ac8a in ovnacts_parse lib/actions.c:4190
      2 0x46ad91 in ovnacts_parse_string lib/actions.c:4208
      3 0x4106d1 in test_parse_actions tests/test-ovn.c:1324
      [...]
  lib/ofp-actions.c:3205:22:
  runtime error: applying non-zero offset 2 to null pointer
      0 0x6e1641 in set_field_split_str lib/ofp-actions.c:3205:22
      [...]
  lib/tnl-ports.c:74:12:
  runtime error: applying zero offset to null pointer
      0 0xceffe7 in tnl_port_cast lib/tnl-ports.c:74:12
      1 0xcf14c3 in map_insert lib/tnl-ports.c:116:13
      [...]
  ofproto/ofproto.c:8905:16:
  runtime error: applying zero offset to null pointer
      0 0x556795 in eviction_group_hash_rule ofproto/ofproto.c:8905:16
      1 0x503f8d in eviction_group_add_rule ofproto/ofproto.c:9022:42
      [...]

Also, it's valid to have an empty ofpact list and we should be able to
try to iterate through it.

UB Sanitizer report:
  include/openvswitch/ofp-actions.h:222:12:
  runtime error: applying zero offset to null pointer
      0 0x665d69 in ofpact_end ./include/openvswitch/ofp-actions.h:222:12
      1 0x66b2cf in ofpacts_put_openflow_actions lib/ofp-actions.c:8861:5
      2 0x6ffdd1 in ofputil_encode_flow_mod lib/ofp-flow.c:447:9
      [...]

Signed-off-by: Dumitru Ceara <[email protected]>
Acked-by: Aaron Conole <[email protected]>
Signed-off-by: Ilya Maximets <[email protected]>
  • Loading branch information
dceara authored and igsilya committed May 17, 2022
1 parent 3764f51 commit 471babb
Show file tree
Hide file tree
Showing 11 changed files with 71 additions and 31 deletions.
4 changes: 3 additions & 1 deletion include/openvswitch/ofp-actions.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,9 @@ struct ofpact *ofpact_next_flattened(const struct ofpact *);
static inline struct ofpact *
ofpact_end(const struct ofpact *ofpacts, size_t ofpacts_len)
{
return ALIGNED_CAST(struct ofpact *, (uint8_t *) ofpacts + ofpacts_len);
return ofpacts
? ALIGNED_CAST(struct ofpact *, (uint8_t *) ofpacts + ofpacts_len)
: NULL;
}

static inline bool
Expand Down
22 changes: 17 additions & 5 deletions include/openvswitch/ofpbuf.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,11 @@ static inline void ofpbuf_delete(struct ofpbuf *b)
static inline void *ofpbuf_at(const struct ofpbuf *b, size_t offset,
size_t size)
{
return offset + size <= b->size ? (char *) b->data + offset : NULL;
if (offset + size <= b->size) {
ovs_assert(b->data);
return (char *) b->data + offset;
}
return NULL;
}

/* Returns a pointer to byte 'offset' in 'b', which must contain at least
Expand All @@ -188,20 +192,23 @@ static inline void *ofpbuf_at_assert(const struct ofpbuf *b, size_t offset,
size_t size)
{
ovs_assert(offset + size <= b->size);
return ((char *) b->data) + offset;
ovs_assert(b->data);
return (char *) b->data + offset;
}

/* Returns a pointer to byte following the last byte of data in use in 'b'. */
static inline void *ofpbuf_tail(const struct ofpbuf *b)
{
return (char *) b->data + b->size;
ovs_assert(b->data || !b->size);
return b->data ? (char *) b->data + b->size : NULL;
}

/* Returns a pointer to byte following the last byte allocated for use (but
* not necessarily in use) in 'b'. */
static inline void *ofpbuf_end(const struct ofpbuf *b)
{
return (char *) b->base + b->allocated;
ovs_assert(b->base || !b->allocated);
return b->base ? (char *) b->base + b->allocated : NULL;
}

/* Returns the number of bytes of headroom in 'b', that is, the number of bytes
Expand Down Expand Up @@ -249,6 +256,11 @@ static inline void *ofpbuf_pull(struct ofpbuf *b, size_t size)
{
ovs_assert(b->size >= size);
void *data = b->data;

if (!size) {
return data;
}

b->data = (char*)b->data + size;
b->size = b->size - size;
return data;
Expand All @@ -270,7 +282,7 @@ static inline struct ofpbuf *ofpbuf_from_list(const struct ovs_list *list)
static inline bool ofpbuf_equal(const struct ofpbuf *a, const struct ofpbuf *b)
{
return a->size == b->size &&
memcmp(a->data, b->data, a->size) == 0;
(a->size == 0 || memcmp(a->data, b->data, a->size) == 0);
}

static inline bool ofpbuf_oversized(const struct ofpbuf *ofpacts)
Expand Down
8 changes: 6 additions & 2 deletions lib/dynamic-string.c
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,10 @@ ds_put_format_valist(struct ds *ds, const char *format, va_list args_)

va_copy(args, args_);
available = ds->string ? ds->allocated - ds->length + 1 : 0;
needed = vsnprintf(&ds->string[ds->length], available, format, args);
needed = vsnprintf(ds->string
? &ds->string[ds->length]
: NULL,
available, format, args);
va_end(args);

if (needed < available) {
Expand All @@ -162,7 +165,8 @@ ds_put_format_valist(struct ds *ds, const char *format, va_list args_)

va_copy(args, args_);
available = ds->allocated - ds->length + 1;
needed = vsnprintf(&ds->string[ds->length], available, format, args);
needed = vsnprintf(&ds->string[ds->length],
available, format, args);
va_end(args);

ovs_assert(needed < available);
Expand Down
4 changes: 3 additions & 1 deletion lib/meta-flow.c
Original file line number Diff line number Diff line change
Expand Up @@ -3442,7 +3442,9 @@ mf_get_vl_mff(const struct mf_field *mff,
const struct vl_mff_map *vl_mff_map)
{
if (mff && mff->variable_len && vl_mff_map) {
return &mf_get_vl_mff__(mff->id, vl_mff_map)->mf;
struct vl_mf_field *vl_mff = mf_get_vl_mff__(mff->id, vl_mff_map);

return vl_mff ? &vl_mff->mf : NULL;
}

return NULL;
Expand Down
11 changes: 8 additions & 3 deletions lib/ofp-actions.c
Original file line number Diff line number Diff line change
Expand Up @@ -3202,16 +3202,21 @@ set_field_split_str(char *arg, char **key, char **value, char **delim)
{
char *value_end;

*key = NULL;
*value = arg;
value_end = strstr(arg, "->");
*key = value_end + strlen("->");
if (delim) {
*delim = value_end;
*delim = NULL;
}

value_end = strstr(arg, "->");
if (!value_end) {
return xasprintf("%s: missing `->'", arg);
}

*key = value_end + strlen("->");
if (delim) {
*delim = value_end;
}
if (strlen(value_end) <= strlen("->")) {
return xasprintf("%s: missing field name following `->'", arg);
}
Expand Down
4 changes: 4 additions & 0 deletions lib/ofpbuf.c
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,10 @@ ofpbuf_reserve(struct ofpbuf *b, size_t size)
void *
ofpbuf_push_uninit(struct ofpbuf *b, size_t size)
{
if (!size) {
return b->data;
}

ofpbuf_prealloc_headroom(b, size);
b->data = (char*)b->data - size;
b->size += size;
Expand Down
37 changes: 21 additions & 16 deletions lib/ovsdb-data.c
Original file line number Diff line number Diff line change
Expand Up @@ -1957,6 +1957,19 @@ ovsdb_datum_add_unsafe(struct ovsdb_datum *datum,
}
}

void
ovsdb_datum_add_from_index_unsafe(struct ovsdb_datum *dst,
const struct ovsdb_datum *src,
size_t idx,
const struct ovsdb_type *type)
{
const union ovsdb_atom *key = &src->keys[idx];
const union ovsdb_atom *value = type->value.type != OVSDB_TYPE_VOID
? &src->values[idx]
: NULL;
ovsdb_datum_add_unsafe(dst, key, value, type, NULL);
}

/* Adds 'n' atoms starting from index 'start_idx' from 'src' to the end of
* 'dst'. 'dst' should have enough memory allocated to hold the additional
* 'n' atoms. Atoms are not cloned, i.e. 'dst' will reference the same data.
Expand Down Expand Up @@ -2165,12 +2178,10 @@ ovsdb_datum_added_removed(struct ovsdb_datum *added,
int c = ovsdb_atom_compare_3way(&old->keys[oi], &new->keys[ni],
type->key.type);
if (c < 0) {
ovsdb_datum_add_unsafe(removed, &old->keys[oi], &old->values[oi],
type, NULL);
ovsdb_datum_add_from_index_unsafe(removed, old, oi, type);
oi++;
} else if (c > 0) {
ovsdb_datum_add_unsafe(added, &new->keys[ni], &new->values[ni],
type, NULL);
ovsdb_datum_add_from_index_unsafe(added, new, ni, type);
ni++;
} else {
if (type->value.type != OVSDB_TYPE_VOID &&
Expand All @@ -2186,13 +2197,11 @@ ovsdb_datum_added_removed(struct ovsdb_datum *added,
}

for (; oi < old->n; oi++) {
ovsdb_datum_add_unsafe(removed, &old->keys[oi], &old->values[oi],
type, NULL);
ovsdb_datum_add_from_index_unsafe(removed, old, oi, type);
}

for (; ni < new->n; ni++) {
ovsdb_datum_add_unsafe(added, &new->keys[ni], &new->values[ni],
type, NULL);
ovsdb_datum_add_from_index_unsafe(added, new, ni, type);
}
}

Expand Down Expand Up @@ -2228,12 +2237,10 @@ ovsdb_datum_diff(struct ovsdb_datum *diff,
int c = ovsdb_atom_compare_3way(&old->keys[oi], &new->keys[ni],
type->key.type);
if (c < 0) {
ovsdb_datum_add_unsafe(diff, &old->keys[oi], &old->values[oi],
type, NULL);
ovsdb_datum_add_from_index_unsafe(diff, old, oi, type);
oi++;
} else if (c > 0) {
ovsdb_datum_add_unsafe(diff, &new->keys[ni], &new->values[ni],
type, NULL);
ovsdb_datum_add_from_index_unsafe(diff, new, ni, type);
ni++;
} else {
if (type->value.type != OVSDB_TYPE_VOID &&
Expand All @@ -2247,13 +2254,11 @@ ovsdb_datum_diff(struct ovsdb_datum *diff,
}

for (; oi < old->n; oi++) {
ovsdb_datum_add_unsafe(diff, &old->keys[oi], &old->values[oi],
type, NULL);
ovsdb_datum_add_from_index_unsafe(diff, old, oi, type);
}

for (; ni < new->n; ni++) {
ovsdb_datum_add_unsafe(diff, &new->keys[ni], &new->values[ni],
type, NULL);
ovsdb_datum_add_from_index_unsafe(diff, new, ni, type);
}
}

Expand Down
4 changes: 4 additions & 0 deletions lib/ovsdb-data.h
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,10 @@ void ovsdb_datum_add_unsafe(struct ovsdb_datum *,
const union ovsdb_atom *value,
const struct ovsdb_type *,
const union ovsdb_atom *range_end_atom);
void ovsdb_datum_add_from_index_unsafe(struct ovsdb_datum *dst,
const struct ovsdb_datum *src,
size_t idx,
const struct ovsdb_type *type);

/* Transactions with named-uuid row names. */
struct json *ovsdb_datum_to_json_with_row_names(const struct ovsdb_datum *,
Expand Down
4 changes: 3 additions & 1 deletion lib/sset.c
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,9 @@ sset_at_position(const struct sset *set, struct sset_position *pos)
struct hmap_node *hmap_node;

hmap_node = hmap_at_position(&set->map, &pos->pos);
return SSET_NODE_FROM_HMAP_NODE(hmap_node);
return hmap_node
? SSET_NODE_FROM_HMAP_NODE(hmap_node)
: NULL;
}

/* Replaces 'a' by the intersection of 'a' and 'b'. That is, removes from 'a'
Expand Down
2 changes: 1 addition & 1 deletion lib/tnl-ports.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ tnl_port_cast(const struct cls_rule *cr)
{
BUILD_ASSERT_DECL(offsetof(struct tnl_port_in, cr) == 0);

return CONTAINER_OF(cr, struct tnl_port_in, cr);
return cr ? CONTAINER_OF(cr, struct tnl_port_in, cr) : NULL;
}

static void
Expand Down
2 changes: 1 addition & 1 deletion ofproto/ofproto.c
Original file line number Diff line number Diff line change
Expand Up @@ -9005,7 +9005,7 @@ eviction_group_hash_rule(struct rule *rule)
hash = table->eviction_group_id_basis;
miniflow_expand(rule->cr.match.flow, &flow);
for (sf = table->eviction_fields;
sf < &table->eviction_fields[table->n_eviction_fields];
sf && sf < &table->eviction_fields[table->n_eviction_fields];
sf++)
{
if (mf_are_prereqs_ok(sf->field, &flow, NULL)) {
Expand Down

0 comments on commit 471babb

Please sign in to comment.