The reason why I have written the way it is written here is that i want it to closely match what was being reviewed and suggested.There were two suggestions which closely matches the two programs I mentioned above.I can't do this way:
char ch[128] = "anish";
As the buffer is provided by userspace to the kernel.We don't know if the buffer provided is null terminated or not.
See, this just tells us that what you're doing does NOT match
what was being reviewed.
Because that suggestion is 100% applicable to the code you
provided. So if it isn't applicable to your real problem,
you need to find a way to express what you are really trying to
do.
You appear to be trying to ensure that a buffer is made to contain
a fixed set of bytes, and you don't care about the bytes after that.
But this comment about the buffer being "provided by userspace"
suggests that you are receiving the address of a buffer somewhere
else.
But if that's the case, then
char ch[128] = { 0 };
is also inapplicable, because that's declaring a local buffer, not
initializing a buffer somewhere else.
I didn't understand this.In the above two programs I wrote both
call the same strncpy function and code is generated for both of
them.
First off, that's already bad -- strncpy() is the wrong function
for almost every task.
So if we remove out the common factors i.e. common code; what
we are left with is the initialized code and explicit null termination
and that is what I was referring here.Disassembled output of both
is given to compare the extra amount of code which gets generated
and the more code means more cycles.No?
Not necessarily. First off, it's very easy to come up with cases where
more code means fewer cycles. Look up "loop unrolling".
Secondly, you're spending an inordinate amount of time looking at
what is probably a miniscule fraction of the runtime, because strcnpy
is much more complicated than everything you're doing.
Thirdly, *the entire concept is fundamentally severely broken*. You
should not be thinking about this *at all*.
Imagine that someone is building a house. What you are doing here is
roughly analagous to that person spending three days with laser levels
and fancy tools trying to make sure that the height of one of the door
latches is precise to within a ten thousandth of an inch, without
even considering things like "is this latch on the side opposite the
hinges, rather than the same side as the hinges"?
Yes, but my situation is different as explained above.
Then no advice you get can possibly be relevant to your actual situation.
/* #1 */
char ch[128] = "anish";
Can't do this.
Then show us ACTUAL CODE EXAMPLES THAT HAVE ANYTHING TO DO WITH WHAT
YOU ARE DOING.
If this is not equivalent to the intent of your other code samples, then
they are wrong in *fundamental* ways which mean that *the generated code
you are looking at has nothing whatsoever to do with the actual code you
care about*.
This is what I did in one of the program but in kernel strcpy doesn't exist and this API is not liked by many people because it opens the gateway for stack overflow attacks.
There is no gateway for stack overflow attacks when using a fixed-length
string literal. (Also, I have no idea what kernel you're working on,
but if it's Linux, pretty sure you could be using str*l*cpy, which has
the distinctive advantage that, unlike str*n*cpy, it's not stupid.)
And if you're not using a fixed-length string literal in your real code,
the most important thing by far is going to be for you to come up with
an example that shows the actual use case and the place your string
actually comes from.
Modern compilers will sometimes do HORRIBLE THINGS to code that
includes string operations and string literals. You should not use
string literals if you want to see what will happen with real code
that isn't using them.
/* #3 */
char ch[128];
strncpy(ch, "anish", 128);
This I can't do.
I like the way you provide a detailed explanation of why you can't
do this.
Situation is different in my case.There were two suggestions as
two programs posted here.So here I am not after fair comparison but
rather comparison in whatever form it is.
And yet, everything an expert can conclude from studying those suggestions
is "impossible" for reasons not adequately explained.
This means those "suggestions" are *not actually your real code*. So nothing
we tell you about them is useful.
If you really had a function:
void foo(void) {
char ch[128];
strncpy(ch, "anish", 5);
ch[5] = '\0';
}
it would be totally legit for a compiler to optimize it into *nothing at
all* because it has no effects that can be observed by a conforming program.
Also, if you're doing kernel code, and allocating a buffer on the stack,
you should perhaps be initializing the whole thing, because you know
what matters more than cycle counts? LEAKING KERNEL STACK SPACE MATTERS
MORE THAN CYCLE COUNTS. If you declare a 128-byte buffer, then write
over the first 6 bytes, you may be returning 122 bytes of kernel stack
data to userspace. That's Very Bad.
What I am looking for is a buffer which has the null terminated string once it is sent by a different function.
Maybe give us an example of an *actual complete function* which does what
you want it to do? Something like:
void populate_buffer(char *src);
char ch[128];
strncpy(ch, src, strlen(src) + 1);
}
or
void populate_buffer(void);
char ch[128];
char *src = other_fn();
strncpy(ch, src, strlen(src) + 1);
}
Because how you get the other string matters. Also it matters how
*absolutely* sure you are that the thing you are getting is actually
a string, and that it is under 128 characters.
See, everything so far has been predicated on the notion that there was
even a tenuous connection between the code you posted and your actual
question. So we were talking about a fixed initializer, known at runtime,
which was a string literal.
If you are getting a buffer from another source, everything changes.
Consider what happens with your happy little program here if the
string you're given is 129 characters long.
I can't do this as I am not sure if example is called with a null terminated string.
....
Dude.
DUDE.
WHAT. THE. ****.
Let us look back at your original example:
void foo()
{
char array[128] = {0};
memcpy(array, "testing", strlen("testing"));
}
Now let us imagine that we are calling this with a string, not using a
string literal:
void foo(char *src)
{
char array[128] = {0};
memcpy(array, src, strlen(src));
}
Similarly:
void foo(char *src)
{
char array[128];
memcpy(array, src, strlen(src));
array[strlen(src)] = '\0';
}
Now.
You just said you *do not know* whether src is null-terminated.
What. Do. You. Think. Happens. If. It's. Not?
The reason I went all the way to "what the ****" with this is:
1. You are working on kernel code.
2. You are apparently calling strlen() on something that *may not be
a null-terminated string*.
3. You are responding to review comments.
4. The review comments are arguing about the cycle count for the
initialization, not the fact that you are apparently calling strlen()
on something which may not be a string.
This is, frankly, absolutely terrifying.
-s