qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PULL v4 23/83] hw/cxl/cdat: CXL CDAT Data Object Exchange implement


From: Jonathan Cameron
Subject: Re: [PULL v4 23/83] hw/cxl/cdat: CXL CDAT Data Object Exchange implementation
Date: Wed, 12 Apr 2023 15:39:57 +0100

On Tue, 11 Apr 2023 16:52:58 +0100
Peter Maydell <peter.maydell@linaro.org> wrote:

> On Mon, 7 Nov 2022 at 22:49, Michael S. Tsirkin <mst@redhat.com> wrote:
> >
> > From: Huai-Cheng Kuo <hchkuo@avery-design.com.tw>
> >
> > The Data Object Exchange implementation of CXL Coherent Device Attribute
> > Table (CDAT). This implementation is referring to "Coherent Device
> > Attribute Table Specification, Rev. 1.03, July. 2022" and "Compute
> > Express Link Specification, Rev. 3.0, July. 2022"
> >
> > This patch adds core support that will be shared by both
> > end-points and switch port emulation.  
> 
> > +static void ct3_load_cdat(CDATObject *cdat, Error **errp)
> > +{
> > +    g_autofree CDATEntry *cdat_st = NULL;
> > +    uint8_t sum = 0;
> > +    int num_ent;
> > +    int i = 0, ent = 1, file_size = 0;
> > +    CDATSubHeader *hdr;
> > +    FILE *fp = NULL;
> > +
> > +    /* Read CDAT file and create its cache */
> > +    fp = fopen(cdat->filename, "r");
> > +    if (!fp) {
> > +        error_setg(errp, "CDAT: Unable to open file");
> > +        return;
> > +    }
> > +
> > +    fseek(fp, 0, SEEK_END);
> > +    file_size = ftell(fp);  
> 
> Coverity points out that ftell() can fail and return -1...
> 
> > +    fseek(fp, 0, SEEK_SET);
> > +    cdat->buf = g_malloc0(file_size);  
> 
> ...which would cause an attempt to allocate a very large
> amount of memory, since you aren't checking for errors.
> CID 1508185.
> 
> Below, some other issues I saw in a quick scan through the code.
> 
> > +
> > +    if (fread(cdat->buf, file_size, 1, fp) == 0) {
> > +        error_setg(errp, "CDAT: File read failed");
> > +        return;
> > +    }  
> 
> (The issues in this bit of code I've mentioned in a
> different thread.)

I'll carry a patch locally using g_file_get_contents()
that gets rid of this mess. My assumption being that similar
will emerge from other thread and I can drop my patch.

> 
> > +
> > +    fclose(fp);
> > +
> > +    if (file_size < sizeof(CDATTableHeader)) {
> > +        error_setg(errp, "CDAT: File too short");
> > +        return;
> > +    }  
> 
> > +    i = sizeof(CDATTableHeader);
> > +    num_ent = 1;
> > +    while (i < file_size) {
> > +        hdr = (CDATSubHeader *)(cdat->buf + i);  
> 
> If the file is not a complete number of records in
> size, then this can index off the end of the buffer.
> You should check for that.

> 
> > +        cdat_len_check(hdr, errp);
> > +        i += hdr->length;
> > +        num_ent++;
> > +    }
> > +    if (i != file_size) {
> > +        error_setg(errp, "CDAT: File length missmatch");  
> 
> Typo: "mismatch"
> 

That's fixed in the tree.

> > +        return;
> > +    }
> > +
> > +    cdat_st = g_malloc0(sizeof(*cdat_st) * num_ent);  
> 
> To allocate an array of N lots of a structure, use
> g_new0(), which will take care to avoid possible
> overflow in the multiply.
> 
> > +    if (!cdat_st) {
> > +        error_setg(errp, "CDAT: Failed to allocate entry array");  
> 
> g_malloc0() and g_new0() can never fail, so you don't need
> to check for a NULL pointer return.

dropped.  I'll remember this one day ;)

> 
> > +        return;
> > +    }
> > +
> > +    /* Set CDAT header, Entry = 0 */
> > +    cdat_st[0].base = cdat->buf;
> > +    cdat_st[0].length = sizeof(CDATTableHeader);
> > +    i = 0;
> > +
> > +    while (i < cdat_st[0].length) {
> > +        sum += cdat->buf[i++];
> > +    }
> > +
> > +    /* Read CDAT structures */
> > +    while (i < file_size) {
> > +        hdr = (CDATSubHeader *)(cdat->buf + i);
> > +        cdat_len_check(hdr, errp);  
> 
> We already did this check the first time through the data...
dropped
> 
> > +
> > +        cdat_st[ent].base = hdr;
> > +        cdat_st[ent].length = hdr->length;
> > +
> > +        while (cdat->buf + i <
> > +               (uint8_t *)cdat_st[ent].base + cdat_st[ent].length) {
> > +            assert(i < file_size);
> > +            sum += cdat->buf[i++];
> > +        }
> > +
> > +        ent++;
> > +    }
> > +
> > +    if (sum != 0) {
> > +        warn_report("CDAT: Found checksum mismatch in %s", cdat->filename);
> > +    }
> > +    cdat->entry_len = num_ent;
> > +    cdat->entry = g_steal_pointer(&cdat_st);
> > +}
> > +
> > +void cxl_doe_cdat_init(CXLComponentState *cxl_cstate, Error **errp)
> > +{
> > +    CDATObject *cdat = &cxl_cstate->cdat;
> > +
> > +    if (cdat->filename) {
> > +        ct3_load_cdat(cdat, errp);
> > +    } else {
> > +        ct3_build_cdat(cdat, errp);
> > +    }
> > +}  
> 
> None of the callsites to this function check for it
> failing. In particular they do not assume "if I call
> this and it fails then I need to call cxl_doe_cdata_release()
> to have it clean up". It would probably be less confusing
> if the init function cleans up after itself, i.e. does not
> leave allocated memory pointed to by cdat->buf and so on.
Agreed.  Will make it cleanup and add the error checks at the two
callers.

Thanks,

Jonathan

> 
> thanks
> -- PMM




reply via email to

[Prev in Thread] Current Thread [Next in Thread]