On Wed, 26 Oct 2016, Christos Zoulas wrote:

In article <pine.neb.4.64.1610260555490.23...@speedy.whooppee.com>,
Paul Goyette  <p...@whooppee.com> wrote:
On Wed, 26 Oct 2016, matthew green wrote:

i think you're right that the 'cp' manipulation is the problem.
snprintf() will return the "desired" size, so upon the first
attempted overflow the 'cp' is moved beyond 'ep', and then the
next snprintf() gets a negative aka extremely massive value
for the buffer length and actual overflow happens here, and ssp
detects it.

the fix would be to change this:

        cp += snprintf(cp, ep - cp, ...);

into this:

        len = snprintf(cp, ep - cp, ...);
        if (len > ep - cp)
                return;
        cp += len;

which is annoying because there are a lot of the former.

There's only 9 snprintf() calls.  I could simply provide a macro:

#define ADD_TEXT(dest, end, format, ...)                        \
        {                                                       \
                int len, max = (end) - (dest);                  \

                len = snprintf((dest), max, (format), __VA_ARGS__); \
                if (len > max)                                       \
                        return;                                 \
                (dest) += len;                                  \
        }


Then all of the snprintf() calls become simply

        ADD_TEXT(cp, ep, <format>, ...);

(Of course, after last use I'd add a #undef ADD_TEXT to clean up...)

Do you really want to stop from attaching the driver because you could
not print it's name? ...

I don't see where this would prevent the device from attaching. It would simply return a truncated description...


... I think that perhaps a variant that returns the
number of characters appended would be more useful. like
if (len >= max)
        dest = end;
else
        dest += len;

but, I would leave the whole thing alone instead :-)
or write a function that appends and moves the pointer, like

snappendprintf(char **dest, const char *end, fmt, ...)


Well, as I have already pointed out to mrg@ in private Email, this change doesn't really fix the problem anyway!

During my earlier debugging I had confirmed that the caller of pci_devinfo() had provided a buffer of size 256, which is more than enough to contain the entire device description. At no time did cp point beyond the end of the buffer, not even at the very end of the function.

The only actual "overflow" that occurs is within pci_findproduct(). It is the call to this function that provides a PCI_PRODUCTSTR_LEN-sized buffer. I have confirmed that pci_findproduct() properly truncates its result and doesn't write beyond the buffer provided. pci_devinfo() provides "char product[PCI_PRODUCTSTR_LEN]" (and similarly vendor[]).

With my specific device (0x8086, 0x6f8a),


        "Core i7-6xxxK/Xeon-D Memory Cont"    /* chars  0 - 31 */
        "roller (Target Address, Thermal,"    /* chars 32 - 63 */
        " RAS)"                                       /* chars 64 - 68 */

and setting the length back to 64, the returned value was truncated (by dev_untokenstring() in src/dev/dev_verbose.c) at the 'm' in "Thermal".

So, I'm still unclear where the stack overflow actually occurs, and until that question is resolved, I'm not going to make any changes!


HOWEVER,

Looking at dev_untokenstring() it would appear that there is a potential problem there! We have

                cp = buf + strlcat(buf, words + *token, len - 2);
                cp[0] = ' ';
                cp[1] = '\0';


Since strlcat(3) (quoting the man page) "return[s] the total length of the string [it] tried to create", it is entirely possible for cp to point beyond the end of the buffer. Thus the insertion of a trailing space-between-word and the NUL character could occur on some random location.

It would seem to me the this code should be written as

                newlen = strlcat(buf, word + *token, len - 2);
                if (newlen > len - 2)
                        newlen = len - 2;
                cp = buf + newlen;
                cp[0] = ' ';
                cp[1] = '\0';

???


+------------------+--------------------------+------------------------+
| Paul Goyette     | PGP Key fingerprint:     | E-mail addresses:      |
| (Retired)        | FA29 0E3B 35AF E8AE 6651 | paul at whooppee.com   |
| Kernel Developer | 0786 F758 55DE 53BA 7731 | pgoyette at netbsd.org |
+------------------+--------------------------+------------------------+

Reply via email to