From: Kushal Kumaran on
On Tue, Jun 29, 2010 at 5:56 AM, Lawrence D'Oliveiro
<ldo(a)geek-central.gen.new_zealand> wrote:
> In message <mailman.2231.1277700501.32709.python-list(a)python.org>, Kushal
> Kumaran wrote:
>
>> On Sun, Jun 27, 2010 at 5:16 PM, Lawrence D'Oliveiro
>> <ldo(a)geek-central.gen.new_zealand> wrote:
>>
>>>In message <mailman.2184.1277626565.32709.python-list(a)python.org>, Kushal
>>> Kumaran wrote:
>>>
>>>> On Sun, Jun 27, 2010 at 9:47 AM, Lawrence D'Oliveiro
>>>> <ldo(a)geek-central.gen.new_zealand> wrote:
>>>>
>>>>> A long while ago I came up with this macro:
>>>>>
>>>>> #define Descr(v) &v, sizeof v
>>>>>
>>>>> making the correct version of the above become
>>>>>
>>>>> snprintf(Descr(buf), foo);
>>>>
>>>> Not quite right.  If buf is a char array, as suggested by the use of
>>>> sizeof, then you're not passing a char* to snprintf.
>>>
>>> What am I passing, then?
>>
>> Here's what gcc tells me (I declared buf as char buf[512]):
>> sprintf.c:8: warning: passing argument 1 of ‘snprintf’ from
>> incompatible pointer type
>> /usr/include/stdio.h:363: note: expected ‘char * __restrict__’ but
>> argument is of type ‘char (*)[512]’
>>
>> You just need to lose the & from the macro.
>
> Why does this work, then:
>
> ldo(a)theon:hack> cat test.c
> #include <stdio.h>
>
> int main(int argc, char ** argv)
>  {
>    char buf[512];
>    const int a = 2, b = 3;
>    snprintf(&buf, sizeof buf, "%d + %d = %d\n", a, b, a + b);
>    fprintf(stdout, buf);
>    return
>        0;
>  } /*main*/
> ldo(a)theon:hack> ./test
> 2 + 3 = 5
>

By accident. I hope your compiler warned you about your snprintf call.

Reading these threads might help you understand how char* and char
(*)[512] are different:

http://groups.google.com/group/comp.lang.c++/browse_thread/thread/24708a9204061ce/848ceaf5ec774d81

http://groups.google.com/group/comp.lang.c.moderated/browse_thread/thread/fe264c550947a2e5/32b330cdf8aba3d6

--
regards,
kushal
From: Mark Lawrence on
On 29/06/2010 01:55, Roy Smith wrote:

[snips]

> The nice thing about null-terminated strings is how portable they have
> been over various word lengths.

The bad thing about null-terminated strings is the number of off-by-one
errors they've helped to create. I obviously have never created an
off-by-one error myself. :)

Kindest regards.

Mark Lawrence.


From: Lawrence D'Oliveiro on
In message <mailman.2332.1277785175.32709.python-list(a)python.org>, Kushal
Kumaran wrote:

> On Tue, Jun 29, 2010 at 5:56 AM, Lawrence D'Oliveiro
> <ldo(a)geek-central.gen.new_zealand> wrote:
>
>> Why does this work, then:
>>
>> ldo(a)theon:hack> cat test.c
>> #include <stdio.h>
>>
>> int main(int argc, char ** argv)
>> {
>> char buf[512];
>> const int a = 2, b = 3;
>> snprintf(&buf, sizeof buf, "%d + %d = %d\n", a, b, a + b);
>> fprintf(stdout, buf);
>> return
>> 0;
>> } /*main*/
>> ldo(a)theon:hack> ./test
>> 2 + 3 = 5
>
> By accident.

I have yet to find an architecture or C compiler where it DOESN'T work.

Feel free to try and prove me wrong.
From: Lawrence D'Oliveiro on
In message <slrni2f8v2.j19.grahn+nntp(a)frailea.sa.invalid>, Jorgen Grahn
wrote:

> On Sat, 2010-06-26, Lawrence D'Oliveiro wrote:
>
>> In message <slrni297ec.1m5.grahn+nntp(a)frailea.sa.invalid>, Jorgen Grahn
>> wrote:
>>
>>> I thought it was well-known that the solution is *not* to try to
>>> sanitize the input -- it's to switch to an interface which doesn't
>>> involve generating an intermediate executable. In the Python example,
>>> that would be something like os.popen2(['zcat', '-f', '--', untrusted]).
>>
>> That's what I mean. Why do people consider input sanitization so hard?
>
> I'm not sure you understood me correctly, because I advocate
> *not* doing input sanitization. Hard or not -- I don't want to know,
> because I don't want to do it.

But no-one has yet managed to come up with an alternative that involves less
work.
From: Michael Torrie on
On 06/29/2010 06:25 PM, Lawrence D'Oliveiro wrote:
> I have yet to find an architecture or C compiler where it DOESN'T work.
>
> Feel free to try and prove me wrong.

Okay, I will. Your code passes a char** when a char* is expected. Every
compiler I know of will give you a *warning*. Mistaking char*, char**,
and char[] is a common mistake that almost every C program makes in the
beginning. Now for the proof:

Consider this variation where I use a dynamically allocated buffer
instead of static:

#include <stdio.h>

int main(int argc, char ** argv)
{
char *buf = malloc(512 * sizeof(char));
const int a = 2, b = 3;
snprintf(&buf, sizeof buf, "%d + %d = %d\n", a, b, a + b);
fprintf(stdout, buf);
free(buf);
return 0;
} /*main*/

On my machine, an immediate segfault (stack overrun). Your code only
works because your buf is statically allocated, which means &buf==buf.
But this equivalance does not hold for any other situation. If your
buffer was dynamically allocated on the heap, instead of passing a
pointer to the buffer (which *is* what buf itself is), you are passing a
pointer to the pointer, which is where buf is stored on the stack, but
not the buffer itself. Instant stack corruption.