A
anish singh
Seebs, first of all I am thankful.You seem toThanks!
have got the problem what I was trying to solve.
I am a new user of C google groups and was of
the opinion that: Let's just post it here and
see what happens without thinking through the whole
program and what kind of reactions that would
generate.However that perception has changed.
I am sure I will do better in next post.
#define ERROR 1#define OK 0
/*userspace wants us to copy size bytes*/int foo(char *buf, int size)
This makes a huge difference, I'd point out: That the size
of the provided string is precomputed changes all sorts of
things. For instance, consider the earlier versions:
/* 1 */
memcpy(space, buf, strlen(buf));
/* 2 */
memcpy(space, buf, strlen(buf));
buf[strlen(buf)] = '\0';
Here, the second one makes two consecutive calls to strlen()
on the same string, so it's going to need computational time
on the close order of 3x the length of the string. The first
is only making one strlen call, and one memcpy call.
But if we change them to:
/* 1 */
memcpy(space, buf, size);
/* 2 */
memcpy(space, buf, size);
buf[size] = '\0';
Now each is only iterating over the string once. Very different!
char string[128];if(size >= 127) {return -ERROR;
* what I want here is the null terminated string* eventhough the caller of foo has not provided* a null terminated string.If size is 126 then* I will set 127 byte to 0.
snprintf(string, size+1, "%s", buf);printf("%s\n", string);return OK;
My biggest concern about this is that typically, when kernel code
is using a local buffer, there is some possibility that the buffer may
later be exposed to user code. If you don't zero out the whole
buffer, you can be exposing stack contents to user code.
int main()
char *string;int size=0;string = malloc(256*sizeof(char));strcpy(string, "anish"); /*set in userspace*//*set in userspace--anything can happen*/
foo(string, size); /* check the return value*/
strcpy(string, "anishkumar");foo(string, size);/* check the return value*/return OK;
Is it a good code?
It makes a little more sense, anyway.
There are still some issues here.
int foo(char *buf, int size)
char string[128];if(size >= 127) {return -ERROR;
* what I want here is the null terminated string* eventhough the caller of foo has not provided* a null terminated string.If size is 126 then* I will set 127 byte to 0.
snprintf(string, size+1, "%s", buf);printf("%s\n", string);return OK;
This has at least a couple of serious potential bugs lurking. The most
obvious is that "int" is a signed type, which means size can be negative.
This came up in code review.Sorry didn't
do that same change here.
I didn't know this.My assumption was this:A negative size isn't >= 127, but snprintf(string, (-2)+1, "%s", buf);
would be at risk of overrunning the buffer fairly dramatically, unless
the snprintf implementation is helpfully realizing you can't possibly
have meant that.
There's another, perhaps more subtle, bug which is that your test should
probably be ">127", not ">=127". You can copy 127 characters, plus a
null byte, into a 128-byte string.
0 to 127 = 128 bytes
If we write anything in 128 location then
it might do something weird.May be a crash.
Great.I didn't know this either.That said, I think you are misunderstanding the point of snprintf.
You should ALWAYS specify the amount of space available to you, not
the amount you think you have, for snprintf. So that line should be:
snprintf(string, sizeof(string), "%s", buf);
If you do this, you are guaranteed that you will not overrun "string",
no matter what value size has. So let's see:
int foo(char *buf, int size)
{
char string[128];
int copied;
if (size > (sizeof(string) - 1)) {
return -ERROR;
}
/* use .* to make snprintf stop immediately if it reaches the end
* of the buffer, so if the user hands us a 32kb string, we don't
* spend a lot of time not-copying it to determine how many bytes
* we'd need to copy.
*/
copied = snprintf(string, sizeof(string), "%.*s", sizeof(string), buf);
if (copied != size) {
/* user lied to us: string was shorter
* or longer than expected. */
return -ERROR;
}
printf("%s\n", string);
return OK;
} Thanks!!
That said... I'd still initialize the local variable. I don't know what
you plan to do with string after copying the user's data into it, but if
there is ANY chance that it would ever be part of something returned to
a user function, it should not have uninitialized stack contents in it.
Good point.
Sorry no idea.I tried gcc and it did as below:You'll note a lot of use of sizeof(string). That's because it is a really
bad habit to rewrite that number. Let's say you start with:
char string[128];
if(size >= 127) {
return -ERROR;
}
Well, somewhere along the line, someone will add an additional test:
char string[128];
if (!buf) {
return -ERROR;
}
if(size >= 127) {
return -ERROR;
}
Now the "127" and "128" aren't adjacent. Then someone else comes along
and says "hang on, we know what's in this string, and it's impossible
for it to ever exceed 60 bytes. Let's leave a little slop value just
in case, but not that much":
char string[80];
if (!buf) {
return -ERROR;
}
if(size >= 127) {
return -ERROR;
}
And now what happens when someone inserts an 87-byte string? *BOOM*.
I guess the key point I want to get at here is:
C does not care what you are trying to illustrate, or what you think is
important, or anything else. C does what you told it to, exactly, whether
or not that is what you want. If you are going to work in C, remember that
C *does not care*. It is not trying to help you. It is not going to catch
your harmless mistakes, it is not going to divine your intent, it is not
going to do what you meant.
It does EXACTLY what you said. Mostly. Unless it can do something totally
different and be sure that, if you are not cheating, you won't be able to
tell. Another good point.
Consider the following code:
int main(void) {
char buf[128];
strcpy(buf, "hello, world!");
printf("%s\n", buf);
return 0;
}
Do you think the code generated by the compiler will include a reference
to strcpy()? I think so.
I'll give you a hint: On the implementation I tried on, there was no
call to either strcpy or printf.
I'll give you two lines of assembly, and see whether you can figure out
why:
3 char buf[128];
4
5 strcpy(buf, "hello, world!");
0x08048477 <+27>: lea 0x1c(%esp),%eax
0x0804847b <+31>: movl $0x6c6c6568,(%eax)
0x08048481 <+37>: movl $0x77202c6f,0x4(%eax)
0x08048488 <+44>: movl $0x646c726f,0x8(%eax)
0x0804848f <+51>: movw $0x21,0xc(%eax)
6
7 printf("%s\n", buf);
0x08048495 <+57>: lea 0x1c(%esp),%eax
0x08048499 <+61>: mov %eax,(%esp)
0x0804849c <+64>: call 0x8048340 <puts@plt>
I can see a call for printf but not for strcpy.
I don't have any remote idea about why?
Well it is a serious issue with big companiesmovabsq $8583909746840200552, %rax ## imm = 0x77202C6F6C6C6568
[...]
callq _puts
What do you think that big number ($85839...) represents? Sorry, no idea.
-s