Skip to contentSkip to conversation

wifi: p54: Annotate struct p54_cal_database with __counted_by

PATCH Superseded Kees Cook 3 participants 6 messages Oct 6, 2023 – Oct 9, 2023 P54 wireless driver

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore

Kees Cook

author
#0 lore raw

PATCH 1/1 wifi: p54: Annotate struct p54_cal_database with __counted_by

Prepare for the coming implementation by GCC and Clang of the __counted_by attribute. Flexible array members annotated with __counted_by can have their accesses bounds-checked at run-time via CONFIG_UBSAN_BOUNDS (for array indexing) and CONFIG_FORTIFY_SOURCE (for strcpy/memcpy-family functions).

As found with Coccinelle[1], add __counted_by for struct p54_cal_database.

---
 drivers/net/wireless/intersil/p54/p54.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/wireless/intersil/p54/p54.h +1 -1
index 3356ea708d81..770e348d1f6c 100644
@@ -126,7 +126,7 @@ struct p54_cal_database {
 	size_t entry_size;
 	size_t offset;
 	size_t len;
-	u8 data[];
+	u8 data[] __counted_by(entries);
 };
 
 #define EEPROM_READBACK_LEN 0x3fc
Signature · 1 line
2.34.1
Reviewed-by on v1 1/1
#1 lore raw
Quoted 8 lines · Kees Cook

Prepare for the coming implementation by GCC and Clang of the __counted_by attribute. Flexible array members annotated with __counted_by can have their accesses bounds-checked at run-time via CONFIG_UBSAN_BOUNDS (for array indexing) and CONFIG_FORTIFY_SOURCE (for strcpy/memcpy-family functions).

As found with Coccinelle[1], add __counted_by for struct p54_cal_database.

Quoted 7 lines

Thanks!

Signature · 1 line
Gustavo
Quoted 17 lines
---
  drivers/net/wireless/intersil/p54/p54.h | 2 +-
  1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/wireless/intersil/p54/p54.h +1 -1
index 3356ea708d81..770e348d1f6c 100644
@@ -126,7 +126,7 @@ struct p54_cal_database {
  	size_t entry_size;
  	size_t offset;
  	size_t len;
-	u8 data[];
+	u8 data[] __counted_by(entries);
  };
 
  #define EEPROM_READBACK_LEN 0x3fc
#2 lore raw
In reply to Kees Cook

Hi,

I randomly peeked at this patch. Unfortunately, I am not familiar with the actual p54 code.

Quoted 25 lines · Kees Cook

Prepare for the coming implementation by GCC and Clang of the __counted_by attribute. Flexible array members annotated with __counted_by can have their accesses bounds-checked at run-time via CONFIG_UBSAN_BOUNDS (for array indexing) and CONFIG_FORTIFY_SOURCE (for strcpy/memcpy-family functions).

As found with Coccinelle[1], add __counted_by for struct p54_cal_database.

---
 drivers/net/wireless/intersil/p54/p54.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/wireless/intersil/p54/p54.h +0 -0
index 3356ea708d81..770e348d1f6c 100644

drivers/net/wireless/intersil/p54/p54.h struct p54_cal_database { · line 129

Quoted 5 lines
drivers/net/wireless/intersil/p54/p54.h +1 -1
@@ -126,7 +126,7 @@ struct p54_cal_database {
        size_t entry_size;
        size_t offset;
        size_t len;
-       u8 data[];
+       u8 data[] __counted_by(entries);

This looks incorrect - I think you want __counted_by(len)? The presence of entry_size made me suspicious.

drivers/net/wireless/intersil/p54/p54.h struct p54_cal_database { · line 130

Quoted 1 line
drivers/net/wireless/intersil/p54/p54.h +0 -0
 };

This is the function that creates struct p54_cal_database:

static struct p54_cal_database *p54_convert_db(struct pda_custom_wrapper *src,
size_t total_len)

{
        struct p54_cal_database *dst;
        size_t payload_len, entries, entry_size, offset;

        payload_len = le16_to_cpu(src->len);
        entries = le16_to_cpu(src->entries);
        entry_size = le16_to_cpu(src->entry_size);
        offset = le16_to_cpu(src->offset);
        if (((entries * entry_size + offset) != payload_len) ||
             (payload_len + sizeof(*src) != total_len))
                return NULL;

        dst = kmalloc(sizeof(*dst) + payload_len, GFP_KERNEL);
        if (!dst)
                return NULL;

        dst->entries = entries;
        dst->entry_size = entry_size;
        dst->offset = offset;
        dst->len = payload_len;

        memcpy(dst->data, src->data, payload_len);
        return dst;
}

You can see that kmalloc is performed with sizeof(*dst) + payload_len, and payload_len is assigned to ->len.

I don't read Coccinelle, but, if this patch was auto-generated, I wonder if the script has an error.

Regards,
Jason

#3 lore raw
Quoted 26 lines · Jason Andryuk

Hi,

I randomly peeked at this patch. Unfortunately, I am not familiar with the actual p54 code.

Quoted 20 lines · Kees Cook

Prepare for the coming implementation by GCC and Clang of the __counted_by attribute. Flexible array members annotated with __counted_by can have their accesses bounds-checked at run-time via CONFIG_UBSAN_BOUNDS (for array indexing) and CONFIG_FORTIFY_SOURCE (for strcpy/memcpy-family functions).

As found with Coccinelle[1], add __counted_by for struct p54_cal_database.

---
  drivers/net/wireless/intersil/p54/p54.h | 2 +-
  1 file changed, 1 insertion(+), 1 deletion(-)
Quoted 12 lines

drivers/net/wireless/intersil/p54/p54.h struct p54_cal_database { · line 129

Quoted 10 lines
drivers/net/wireless/intersil/p54/p54.h +1 -1
index 3356ea708d81..770e348d1f6c 100644
@@ -126,7 +126,7 @@ struct p54_cal_database {
         size_t entry_size;
         size_t offset;
         size_t len;
-       u8 data[];
+       u8 data[] __counted_by(entries);

This looks incorrect - I think you want __counted_by(len)? The

I think you're right. More comments below...

Quoted 33 lines

presence of entry_size made me suspicious.

drivers/net/wireless/intersil/p54/p54.h struct p54_cal_database { · line 130

Quoted 1 line
drivers/net/wireless/intersil/p54/p54.h +0 -0
  };

This is the function that creates struct p54_cal_database:

static struct p54_cal_database *p54_convert_db(struct pda_custom_wrapper *src,
size_t total_len)

{
         struct p54_cal_database *dst;
         size_t payload_len, entries, entry_size, offset;

         payload_len = le16_to_cpu(src->len);
         entries = le16_to_cpu(src->entries);
         entry_size = le16_to_cpu(src->entry_size);
         offset = le16_to_cpu(src->offset);
         if (((entries * entry_size + offset) != payload_len) ||
              (payload_len + sizeof(*src) != total_len))
                 return NULL;

         dst = kmalloc(sizeof(*dst) + payload_len, GFP_KERNEL);
         if (!dst)
                 return NULL;

         dst->entries = entries;
         dst->entry_size = entry_size;
         dst->offset = offset;
         dst->len = payload_len;

         memcpy(dst->data, src->data, payload_len);
         return dst;
}
Quoted 2 lines

You can see that kmalloc is performed with sizeof(*dst) + payload_len, and payload_len is assigned to ->len.

This should be changed to:

- dst = kmalloc(sizeof(*dst) + payload_len, GFP_KERNEL);
+ dst = kmalloc(struct_size(dst, data, payload_len), GFP_KERNEL);

Quoted 3 lines

I don't read Coccinelle, but, if this patch was auto-generated, I wonder if the script has an error.

With the struct_size() change, the Coccinelle script should be able to generate a correct patch for this.

--
Gustavo

author
#4 lore raw
In reply to Jason Andryuk
Quoted 71 lines · Jason Andryuk

Hi,

I randomly peeked at this patch. Unfortunately, I am not familiar with the actual p54 code.

Quoted 25 lines · Kees Cook

Prepare for the coming implementation by GCC and Clang of the __counted_by attribute. Flexible array members annotated with __counted_by can have their accesses bounds-checked at run-time via CONFIG_UBSAN_BOUNDS (for array indexing) and CONFIG_FORTIFY_SOURCE (for strcpy/memcpy-family functions).

As found with Coccinelle[1], add __counted_by for struct p54_cal_database.

---
 drivers/net/wireless/intersil/p54/p54.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/wireless/intersil/p54/p54.h +0 -0
index 3356ea708d81..770e348d1f6c 100644

drivers/net/wireless/intersil/p54/p54.h struct p54_cal_database { · line 129

Quoted 5 lines
drivers/net/wireless/intersil/p54/p54.h +1 -1
@@ -126,7 +126,7 @@ struct p54_cal_database {
        size_t entry_size;
        size_t offset;
        size_t len;
-       u8 data[];
+       u8 data[] __counted_by(entries);

This looks incorrect - I think you want __counted_by(len)? The presence of entry_size made me suspicious.

drivers/net/wireless/intersil/p54/p54.h struct p54_cal_database { · line 130

Quoted 1 line
drivers/net/wireless/intersil/p54/p54.h +0 -0
 };

This is the function that creates struct p54_cal_database:

static struct p54_cal_database *p54_convert_db(struct pda_custom_wrapper *src,
size_t total_len)

{
        struct p54_cal_database *dst;
        size_t payload_len, entries, entry_size, offset;

        payload_len = le16_to_cpu(src->len);
        entries = le16_to_cpu(src->entries);
        entry_size = le16_to_cpu(src->entry_size);
        offset = le16_to_cpu(src->offset);
        if (((entries * entry_size + offset) != payload_len) ||
             (payload_len + sizeof(*src) != total_len))
                return NULL;

        dst = kmalloc(sizeof(*dst) + payload_len, GFP_KERNEL);
        if (!dst)
                return NULL;

        dst->entries = entries;
        dst->entry_size = entry_size;
        dst->offset = offset;
        dst->len = payload_len;

        memcpy(dst->data, src->data, payload_len);
        return dst;
}
Quoted 2 lines

You can see that kmalloc is performed with sizeof(*dst) + payload_len, and payload_len is assigned to ->len.

Agreed!

Quoted 2 lines

I don't read Coccinelle, but, if this patch was auto-generated, I wonder if the script has an error.

Yeah, I'm not sure which part went wrong. I will investigate. Thanks for catching this!

Signature · 1 line
Kees Cook
author
#5 lore raw
In reply to Jason Andryuk
Quoted 29 lines · Jason Andryuk

This is the function that creates struct p54_cal_database:

static struct p54_cal_database *p54_convert_db(struct pda_custom_wrapper *src,
size_t total_len)

{
        struct p54_cal_database *dst;
        size_t payload_len, entries, entry_size, offset;

        payload_len = le16_to_cpu(src->len);
        entries = le16_to_cpu(src->entries);
        entry_size = le16_to_cpu(src->entry_size);
        offset = le16_to_cpu(src->offset);
        if (((entries * entry_size + offset) != payload_len) ||
             (payload_len + sizeof(*src) != total_len))
                return NULL;

        dst = kmalloc(sizeof(*dst) + payload_len, GFP_KERNEL);
        if (!dst)
                return NULL;

        dst->entries = entries;
        dst->entry_size = entry_size;
        dst->offset = offset;
        dst->len = payload_len;

        memcpy(dst->data, src->data, payload_len);
        return dst;
}
Quoted 5 lines

You can see that kmalloc is performed with sizeof(*dst) + payload_len, and payload_len is assigned to ->len.

I don't read Coccinelle, but, if this patch was auto-generated, I wonder if the script has an error.

It seems that my Coccinelle script got confused by this:

p54_convert_output_limits():

        priv->output_limit = kmalloc(data[1] *
                sizeof(struct pda_channel_output_limit) +
                sizeof(*priv->output_limit), GFP_KERNEL);

	...

        priv->output_limit->entry_size =
                sizeof(struct pda_channel_output_limit);
        priv->output_limit->len = priv->output_limit->entry_size *
                                  priv->output_limit->entries +
                                  priv->output_limit->offset;

It thought "sizeof(struct pda_channel_output_limit)" was the element count, since it wasn't able to identify the array member here.

Regardless, I've sent a v2 now. :)

Signature · 1 line
Kees Cook