Modify pointer value in Linux kernel - c

Firstly, I've create a simple program in C
unsigned char * text ="Test program";
int _size = strlen(text);
unsigned char * str = malloc(sizeof(text));
memcpy(str, text, _size);
printf("Before(%d): %s\n", _size, str);
for(i=0;i < _size; i++) {
str[i] -= 13; //rot13
}
printf("After: (%d): %s\n", strlen(str), str);
It runs properly. However, when I move this code to Linux kernel, it seems to fail to work
unsigned char * str;
len = min(count, log->size - read->off);
/* Allocate the memory for storing plain text */
str = kmalloc(len, GFP_KERNEL);
if(str == NULL) {
printk(KERN_ERR "logger: failed to allocate buffer\n");
return -ENOMEM;
}
memcpy(str, log->buf + read->off, len);
/* Start: Add a simple rot13 encryption here */
for(i=0;i < strlen(str); i++)
str[i] -= 13; //rot13
/* End: Add a simple rot13 encryption here */
if (copy_to_user(buf, str, len))
return -EFAULT;
if(str != NULL) {
kfree(str);
}
The problem comes from following code
for(i=0;i < strlen(str); i++)
str[i] -= 13; //rot13
Because if it's removed, program runs as original case. Did I miss something here?

The problem: sizeof(text) returns the size of the pointer, and not the length of the string text points to. Also remember that all string have an extra character that terminates the string. This all means that you write to, and read from, beyond the memory you allocated, and that is undefined behavior which means anything could happen.
Also, literal strings are actually constant (const char *).
And lastly, you might want to read about ROT13, as what you're doing is not ROT13 encryption.

You haven't terminated str with a '\0' so you're most likely just running off the end of the buffer and stomping over memory.
Change:
str = kmalloc(len, GFP_KERNEL);
to:
str = kmalloc(len + 1, GFP_KERNEL); // allocate additional char for terminator
and change:
memcpy(str, log->buf + read->off, len);
to:
memcpy(str, log->buf + read->off, len);
str[len] = '\0'; // put terminator at end of string

If you are dealing with strings try using strncpy() instead of memcpy. 'coz that will put a NULL char at the end automatically and you are safe from buffer over runs. But in this case, i'm not very sure what exactly is the problem, unless you give more details about the issue.
And for any kernel programming errors, the key is to add debugs/printks and collect as much as data about what is happening. If thats not helping you debug yourself, that will help others to help you better.

Related

malloc'd pointer resetting itself to NULL?

I am getting some pretty strange behavior using malloc. I am never allocating more than 4kB, so this seems especially strange to me. My main looks something like:
int main(int argc, char **argv)
{
char *buf;
char *raw = malloc(1024);
fgets(raw, 1024, stdin);
parse(raw, &buf); // Process raw input, get parse length
printf("raw: 0x%p\n", raw); // Outputs 0x00000000
if(raw != NULL)
{ // Only prints when less than 10 characters are entered
free(raw);
printf("raw is not NULL\n");
}
free(buf);
return 0;
}
When I enter less than 10 characters this works okay, when I enter exactly 10 characters I get a segmentation fault, and when I enter more than 10 the output shows that raw is NULL. It should be noted that the size of raw is 1024 malloc'd bytes, so I should have more room to work with.
The parse function is:
int parse(char *src, char **dst)
{
int num_valid = 0, len = strlen(src), j = 0;
// Count number of valid characters
for(int i = 0; i < len; i++)
{
if(src[i] == 'A')
++num_valid;
}
*dst = malloc(num_valid);
for(int i = 0; i < len; i++)
{
if(src[i] == 'A')
*dst[j++] = src[i];
}
// For debugging:
printf("src: 0x%p\n", src); // outputs correct address
return num_valid;
}
This function outputs the correct address, and properly allocates and fills dst. I modified the code here slightly, this is basically a very reduced form of my code. I compiled and ran it (gcc test.c -Werror -Wall) with the same results. It is only after this function returns that my raw pointer becomes NULL, or I get a segfault.
Can someone point me in the right direction? Tell me what exactly I am doing wrong? I've been debugging this little piece of code since yesterday and it is driving me mad.
This doesn't mean what you think it means:
*dst[j++] = src[i];
You meant
(*dst)[j++] = src[i];
What you wrote means:
*(dst[j++]) = src[i];
dst[1] is whatever happens to follow the variable buf, so you're using an address from a random memory location and overwriting whatever it might point to; that's undefined behaviour.
As #pm100 points out in a comment, keeping the buffer in a temporary variable is generally better style:
char* buf = malloc(num_valid);
if (!buf) { /* Handle allocation failure */ }
*dst = buf;
/* ... */
/* In loop */
buf[j++] = src[i];

Appending a char to a char*

I'm writing a program and I need to append a char to a char*. I have a char** that represents lines of text. I'm trying to take a single char at a time from char**, and add it to a char*.
So basically, I tried strcat(char*, char[i][j]), but strcat rejects char[i][j] since it's not a pointer. I think I need to use sprintf(char*, "%c", char[i][j]), but I'm having trouble understanding how to append with sprintf. I don't want it to overwrite what's already in my char*.
Any tips?
You almost got it!
strncat (char *, & char[i][j], 1);
Calling strncat like that will copy exactly one char from your char** at position [i][j]. Just remember that it will also append the null character after that.
given that you know that buf is defined as
char buf [MAXSIZE];
Let us assume that is has been initialized to an initial string and you want to add mychar to it (a single character)
i = strlen(buf);
if (i < MAXSIZE-1)
{
buf[i] = mychar; // Make the new character the last element of buf
buf[i+1] = '\0' // end the new string with the null character
}
This will append the new character and push the ending null character to ensure that it is a valid string. This is what strcat does when the appended entry is also a string or what strncat does when the count is 1. Note that the new strlen(buf) will now be i+1
Assuming buf is a 0 terminated c-string, you can use snprintf():
#include <stdio.h>
#include <string.h>
int main(void)
{
size_t b_len;
char buf[256];
char ch = '!';
strcpy(buf, "Hello world");
b_len = strlen(buf);
snprintf(buf + b_len, sizeof buf - b_len, "%c", ch);
printf("%s", buf);
char *p = malloc(256); /* check if malloc failed */
strcpy(p, "Hello world");
size_t len = strlen(p);
snprintf(p + len, 256 - len, "%c", ch);
printf("%s", p);
return 0;
}
If buf happens to be pointer (such as an malloced pointer) then you can't use sizeof (it can only be used on an array to get the size).
I have a very hard time understanding your question, instead of posting an example of what you want to achieve you explained something that seems to be based on your way of solving a problem with very little training and/or experience in the c language. So it's hard to write a good answer but definitely I will try to show you a way of doing something similar to what apparently you want to do.
The thing is, I highly doubt that strcat() is appropriate for this or for anything except, concatenating two strings. More than two, require something better than strcat(). And for a single char it's definitely less appropriate.
The following code appends all the characters in the string literal array into a single string, check it out
int
main(int argc, char **argv)
{
char string[100];
char lines[3][14] = {
"First line...",
"Another line.",
"One more line"
};
size_t i;
i = 0;
for (size_t j = 0 ; ((j < sizeof(lines) / sizeof(lines[0])) && (i < sizeof(string) - 1)) ; ++j)
{
for (size_t k = 0 ; ((k < 13) && (i < sizeof(string) - 1)) ; ++k)
string[i++] = lines[j][k];
}
string[i] = '\0';
puts(string);
return 0;
}

Reading from socket in C giving weird output

I'm having trouble reading from a socket. The code I'm using is below, sometimes it works just fine, but at other times, it just prints some unreadable characters, or some random readable ones... is there a better way?
char* answer = (char*) malloc(1024);
int answerLength = 0;
char prevChar = 0;
char newChar = 0;
while (answerLength < 1024 && read(sock, &newChar, 1) > 0 ) {
if (newChar == '\n' && prevChar == '\r') {
break;
}
printf("%d\n", answerLength);
answer[ answerLength ] = newChar;
answerLength++;
prevChar = newChar;
}
printf("%s\n", answer);
Strings in C must be null-terminated, which means they must have a symbol \0 as the last character.
Since you don't guarantee that it will happen anywhere in your code, answer may be padded with memory garbage next to the data you read.
To make sure it won't happen, use:
answer[answerLength] = '\0';
printf("%s\n", answer);
Also, you could just read() the whole thing straight to answer, you don't need that pointless loop:
int len;
while (len = read(sock, &answer[answerLength], 1024 - answerLength))
answerLength += len;
answer[answerLength] = '\0';
printf("%s\n", answer);
The data you read isn't terminated with a '\0' character, so you can't treat is as a string.
Your char array is not guaranteed to be null terminated. This means that the printf may print more than just what is in your array since it looks for a null termination to stop outputting characters.
You also don't initialise the allocated memory before using it, which is bad practice since the memory can contain random garbage.
To get the code to work better and hopefully fix your problem, you should do the following:
char* answer = malloc(1024 + 1); /* add extra byte for null terminator */
/* other variables the same */
memset( answer, '\0', 1024 + 1 ); /* initialise memory before use */
while (answerLength < 1024 && read(sock, &newChar, 1) > 0 ) {
/* loop is the same */
}
printf("%s\n", answer);
There is also an argument in printf which will tell it to print a certain number of characters. Like so:
printf( "%.*s\n", answerLength, answer );

C: counting the string's length before using scanf

I am trying to find a way to manipulate strings in C in a more efficient way (maybe like how java does it).
One way I thought of it is to count the size of the string till the end of the line (maybe including spaces), allocate memory of this size using malloc() and then go back to the beginning of the line and scan the string.
Is there a way to do this? I don't know if there is a way to return the "cursor" to the beginning of the line to 're'scan something.
And if you know another/better way to deal with strings in C please tell me.
Thanks
There is no way to do what you're asking directly, but there is a (in my opinion far better) alternative: fgets().
What it does is read the text until the end of the line, including the final line-feed. If the line is longer than the buffer, then it omits that line feed --- you can use that fact to check if the line was completed.
Something like this (UNTESTED CODE):
// WARNING: Example does not include error checking
// (check the return value of `fgets()`, `malloc()` and `realloc()`!)
size_t buflen = 64;
size_t pos = 0;
char* buf = malloc(buflen);
// `for(;;)` is an infinite loop
for(;;)
{
// read data into buf[pos..buflen] (total of `buflen-pos` bytes)
fgets(buf + pos, buflen - pos, file);
pos = pos + strcspn(buf + pos, "\r\n");
if(buf[pos]) // reached end of line; end the loop
break;
buflen += 64;
// alternative (double the size):
// buflen <<= 1;
buf = realloc(buf, buflen); // resize the buffer
}
// `buf` contains our line; `pos` contains the end of it
// optional: remove the trailing newline
// buf[pos] = 0;
Relevant documentation:
fgets()
strcspn()
malloc()
realloc()
You could use scanf to read every character and then add that character into your buffer.
Your buffer initial size could be 16. And after you read every character you check if you have space for that new character. If you do not have space for your new character you double buffer size and realloc it.
Check out the code example:
#include <stdio.h>
#include <stdlib.h>
char *str;
int main(void) {
char c = '\0';
int size = 0;
int buffer_size = 16;
str = (char *) calloc(buffer_size, sizeof(char));
while (c != '\n') {
scanf("%c", &c);
if (size + 1 == buffer_size) {
buffer_size *= 2;
str = (char *) realloc(str, buffer_size);
if (str == NULL) {
fprintf(stderr, "insufficient memory\n");
return EXIT_FAILURE;
}
}
str[size] = c;
size++;
}
printf("%s\n", str);
return EXIT_SUCCESS;
}

append a character from an array to a char pointer

Ok, so I'm a person who usually writes Java/C++, and I've just started getting into writing C. I'm currently writing a lexical analyser, and I can't stand how strings work in C, since I can't perform string arithmetic. So here's my question:
char* buffer = "";
char* line = "hello, world";
int i;
for (i = 0; i < strlen(line); i++) {
buffer += line[i];
}
How can I do that in C? Since the code above isn't valid C, how can I do something like that?
Basically I'm looping though a string line, and I'm trying to append each character to the buffer string.
string literals are immutable in C. Modifying one causes Undefined Behavior.
If you use a char array (your buffer) big enough to hold your characters, you can still modify its content :
#include <stdio.h>
int main(void) {
char * line = "hello, world";
char buffer[32]; // ok, this array is big enough for our operation
int i;
for (i = 0; i < strlen(line) + 1; i++)
{
buffer[i] = line[i];
}
printf("buffer : %s", buffer);
return 0;
}
First off the buffer needs to have or exceed the length of the data being copied to it.
char a[length], b[] = "string";
Then the characters are copied to the buffer.
int i = 0;
while (i < length && b[i] != '\0') { a[i] = b[i]; i++; }
a[i] = '\0';
You can reverse the order if you need to, just start i at the smallest length value among the two strings, and decrement the value instead of increment. You can also use the heap, as others have suggested, ordinate towards an arbitrary or changing value of length. Furthermore, you can change up the snippet with pointers (and to give you a better idea of what is happening):
int i = 0;
char *j = a, *k = b;
while (j - a < length && *k) { *(j++) = *(k++); }
*j = '\0';
Make sure to look up memcpy; and don't forget null terminators (oops).
#include <string.h>
//...
char *line = "hello, world";
char *buffer = ( char * ) malloc( strlen( line ) + 1 );
strcpy( buffer, line );
Though in C string literals have types of non-const arrays it is better to declare pointers initialized by string literals with qualifier const:
const char *line = "hello, world";
String literals in C/C++ are immutable.
If you want to append characters then the code can look the following way (each character of line is appended to buffer in a loop)
#include <string.h>
//...
char *line = "hello, world";
char *buffer = ( char * ) malloc( strlen( line ) + 1 );
buffer[0] = '\0';
char *p = Buffer;
for ( size_t i = 0; i < strlen( line ); i++ )
{
*p++ = line[i];
*p = '\0';
}
The general approach is that you find the pointer to the terminating zero substitute it for the target character advance the pointer and appenf the new terminating zero. The source buffer shall be large enough to accomodate one more character.
If you want to append a single character to a string allocated on the heap, here's one way to do it:
size_t length = strlen(buffer);
char *newbuffer = realloc(buffer, length + 2);
if (newbuffer) { // realloc succeeded
buffer = newbuffer;
buffer[length] = newcharacter;
buffer[length + 1] = '\0';
}
else { // realloc failed
// TODO handle error...
free(buffer); // for example
}
However, this is inefficient to do repeatedly in a loop, because you'll be repeatedly calling strlen() on (essentially) the same string, and reallocating the buffer to fit one more character each time.
If you want to be smarter about your reallocations, keep track of the buffer's current allocated capacity separately from the length of the string within it — if you know C++, think of the difference between a std::string object's "size" and its "capacity" — and when it's necessary to reallocate, multiply the buffer's size by a scaling factor (e.g. double it) instead of adding 1, so that the number of reallocations is O(log n) instead of O(n).
This is the sort of thing that a good string class would do in C++. In C, you'll probably want to move this buffer-management stuff into its own module.
The simplest solution, lacking any context, is to do:
char buffer[ strlen(line) + 1 ];
strcpy(buffer, line);
You may be used to using pointers for everything in Java (since non-primitive types in Java are actually more like shared pointers than anything else). However you don't necessarily have to do this in C and it can be a pain if you do.
Maybe a good idea given your background would be to use a counted string object in C, where the string object owns its data. Write struct my_string { char *data; size_t length; } . Write functions for creating, destroying, duplicating, and any other operation you need such as appending a character, or checking the length. (Separate interface from implementation!) A useful addition to this would be to make it allocate 1 more byte than length, so that you can have a function which null-terminates and allows it to be passed to a function that expects a read-only C-style string.
The only real pitfall here is to remember to call a function when you are doing a copy operation, instead of allowing structure assignment to happen. (You can use structure assignment for a move operation of course!)
The asprintf function is very useful for building strings, and is available on GNU-based systems (Linux), or most *BSD based systems. You can do things like:
char *buffer;
if (asprintf(&buffer, "%s: adding some stuff %d - %s", str1, number, str2) < 0) {
fprintf(stderr, "Oops -- out of memory\n");
exit(1); }
printf("created the string \"%s\"\n", buffer);
free(buffer); /* done with it */
Appending is best done with snprintf
Include the stdio.h header
#include <stdio.h>
then
char* buffer;
char line[] = "hello, world";
// Initialise the pointer to an empty string
snprintf(buffer, 1, "%s", "");
for (i = 0; i < strlen(line); ++i) {
snprintf(buffer, sizeof line[i], "%s%s", buffer, line[i]);
}
As you have started the code you have there is different from the question you are asking.
You could have split the line with strtok though.
But I hope my answer clarifies it.

Resources