Special character length - c

I found something really strange today.
In the code above, I saw that utrlen(&eot) (utrlen == strlen) is equal to two, and utrlen(&etx) == 1.
The best part : when I swap the order of declaration with etx, utrlen(&eot) == 1 and utrlen(&etx) == 2...
char **get_ukey_string()
{
static char eot = 0x4;
static char etx = 0x3;
static char *ukey_string[NB_UKEY] = {
"\b", "\r", "[B", "\0", "\n", "[D", "[C", "[A", &etx, &eot
};
return (ukey_string);
}
t_hashtable *new_ukey_htable(int fd)
{
char **ukey_string;
t_hashtable *htable;
t_hashnode *hnode;
char *key;
unsigned int i;
size_t size;
if ((htable = new_hashtable(NB_UKEY)) == NULL)
return (NULL);
ukey_string = get_ukey_string();
i = 2;
while (i < NB_UKEY + 2)
{
key = ukey_string[i - 2];
size = utrlen(key);
if (((hnode = new_hashnode(sutrdup(key, size), size, i)) == NULL)
|| htable->add_node(htable, hnode))
{
delete_hashtable(htable);
return (NULL);
}
++i;
}
return (htable);
}
Does anyone have an idea why?

eot and etx are char, not char *, so you can't apply strlen on them because there are no null terminator.

The strlen function (why are you calling it utrlen?) takes an argument of type const char*. That argument must point to the initial character of a string, defined as "a contiguous sequence of characters terminated by and including the first null character".
Calling strlen with the address of a declared char object is legal; the address type char* (pointer to char) matches the required type to be passed to strlen. But it's not the initial character of a string, so the behavior is undefined.
In practice, strlen will start at the char object whose address you gave it, and iterate until it sees a null character '\0'.
If the char object happens to have the value '\0', it finds it immediately and returns 0 -- which is not particularly useful.
If it has some other value, strlen will attempt to scan bytes in memory that aren't part of the object. The result is undefined behavior.
Don't do that.

Related

Cannot access empty string from array of strings in C

I'm using an array of strings in C to hold arguments given to a custom shell. I initialize the array of buffers using:
char *args[MAX_CHAR];
Once I parse the arguments, I send them to the following function to determine the type of IO redirection if there are any (this is just the first of 3 functions to check for redirection and it only checks for STDIN redirection).
int parseInputFile(char **args, char *inputFilePath) {
char *inputSymbol = "<";
int isFound = 0;
for (int i = 0; i < MAX_ARG; i++) {
if (strlen(args[i]) == 0) {
isFound = 0;
break;
}
if ((strcmp(args[i], inputSymbol)) == 0) {
strcpy(inputFilePath, args[i+1]);
isFound = 1;
break;
}
}
return isFound;
}
Once I compile and run the shell, it crashes with a SIGSEGV. Using GDB I determined that the shell is crashing on the following line:
if (strlen(args[i]) == 0) {
This is because the address of arg[i] (the first empty string after the parsed commands) is inaccessible. Here is the error from GDB and all relevant variables:
(gdb) next
359 if (strlen(args[i]) == 0) {
(gdb) p args[0]
$1 = 0x7fffffffe570 "echo"
(gdb) p args[1]
$2 = 0x7fffffffe575 "test"
(gdb) p args[2]
$3 = 0x0
(gdb) p i
$4 = 2
(gdb) next
Program received signal SIGSEGV, Segmentation fault.
parseInputFile (args=0x7fffffffd570, inputFilePath=0x7fffffffd240 "") at shell.c:359
359 if (strlen(args[i]) == 0) {
I believe that the p args[2] returning $3 = 0x0 means that because the index has yet to be written to, it is mapped to address 0x0 which is out of the bounds of execution. Although I can't figure out why this is because it was declared as a buffer. Any suggestions on how to solve this problem?
EDIT: Per Kaylum's comment, here is a minimal reproducible example
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include <sys/stat.h>
#include<readline/readline.h>
#include<readline/history.h>
#include <fcntl.h>
// Defined values
#define MAX_CHAR 256
#define MAX_ARG 64
#define clear() printf("\033[H\033[J") // Clear window
#define DEFAULT_PROMPT_SUFFIX "> "
char PROMPT[MAX_CHAR], SPATH[1024];
int parseInputFile(char **args, char *inputFilePath) {
char *inputSymbol = "<";
int isFound = 0;
for (int i = 0; i < MAX_ARG; i++) {
if (strlen(args[i]) == 0) {
isFound = 0;
break;
}
if ((strcmp(args[i], inputSymbol)) == 0) {
strcpy(inputFilePath, args[i+1]);
isFound = 1;
break;
}
}
return isFound;
}
int ioRedirectHandler(char **args) {
char inputFilePath[MAX_CHAR] = "";
// Check if any redirects exist
if (parseInputFile(args, inputFilePath)) {
return 1;
} else {
return 0;
}
}
void parseArgs(char *cmd, char **cmdArgs) {
int na;
// Separate each argument of a command to a separate string
for (na = 0; na < MAX_ARG; na++) {
cmdArgs[na] = strsep(&cmd, " ");
if (cmdArgs[na] == NULL) {
break;
}
if (strlen(cmdArgs[na]) == 0) {
na--;
}
}
}
int processInput(char* input, char **args, char **pipedArgs) {
// Parse the single command and args
parseArgs(input, args);
return 0;
}
int getInput(char *input) {
char *buf, loc_prompt[MAX_CHAR] = "\n";
strcat(loc_prompt, PROMPT);
buf = readline(loc_prompt);
if (strlen(buf) != 0) {
add_history(buf);
strcpy(input, buf);
return 0;
} else {
return 1;
}
}
void init() {
char *uname;
clear();
uname = getenv("USER");
printf("\n\n \t\tWelcome to Student Shell, %s! \n\n", uname);
// Initialize the prompt
snprintf(PROMPT, MAX_CHAR, "%s%s", uname, DEFAULT_PROMPT_SUFFIX);
}
int main() {
char input[MAX_CHAR];
char *args[MAX_CHAR], *pipedArgs[MAX_CHAR];
int isPiped = 0, isIORedir = 0;
init();
while(1) {
// Get the user input
if (getInput(input)) {
continue;
}
isPiped = processInput(input, args, pipedArgs);
isIORedir = ioRedirectHandler(args);
}
return 0;
}
Note: If I forgot to include any important information, please let me know and I can get it updated.
When you write
char *args[MAX_CHAR];
you allocate room for MAX_CHAR pointers to char. You do not initialise the array. If it is a global variable, you will have initialised all the pointers to NULL, but you do it in a function, so the elements in the array can point anywhere. You should not dereference them before you have set the pointers to point at something you are allowed to access.
You also do this, though, in parseArgs(), where you do this:
cmdArgs[na] = strsep(&cmd, " ");
There are two potential issues here, but let's deal with the one you hit first. When strsep() is through the tokens you are splitting, it returns NULL. You test for that to get out of parseArgs() so you already know this. However, where your program crashes you seem to have forgotten this again. You call strlen() on a NULL pointer, and that is a no-no.
There is a difference between NULL and the empty string. An empty string is a pointer to a buffer that has the zero char first; the string "" is a pointer to a location that holds the character '\0'. The NULL pointer is a special value for pointers, often address zero, that means that the pointer doesn't point anywhere. Obviously, the NULL pointer cannot point to an empty string. You need to check if an argument is NULL, not if it is the empty string.
If you want to check both for NULL and the empty string, you could do something like
if (!args[i] || strlen(args[i]) == 0) {
If args[i] is NULL then !args[i] is true, so you will enter the if body if you have NULL or if you have a pointer to an empty string.
(You could also check the empty string with !(*args[i]); *args[i] is the first character that args[i] points at. So *args[i] is zero if you have the empty string; zero is interpreted as false, so !(*args[i]) is true if and only if args[i] is the empty string. Not that this is more readable, but it shows again the difference between empty strings and NULL).
I mentioned another issue with the parsed arguments. Whether it is a problem or not depends on the application. But when you parse a string with strsep(), you get pointers into the parsed string. You have to be careful not to free that string (it is input in your main() function) or to modify it after you have parsed the string. If you change the string, you have changed what all the parsed strings look at. You do not do this in your program, so it isn't a problem here, but it is worth keeping in mind. If you want your parsed arguments to survive longer than they do now, after the next command is passed, you need to copy them. The next command that is passed will change them as it is now.
In main
char input[MAX_CHAR];
char *args[MAX_CHAR], *pipedArgs[MAX_CHAR];
are all uninitialized. They contain indeterminate values. This could be a potential source of bugs, but is not the reason here, as
getInput modifies the contents of input to be a valid string before any reads occur.
pipedArgs is unused, so raises no issues (yet).
args is modified by parseArgs to (possibly!) contain a NULL sentinel value, without any indeterminate pointers being read first.
Firstly, in parseArgs it is possible to completely fill args without setting the NULL sentinel value that other parts of the program should rely on.
Looking deeper, in parseInputFile the following
if (strlen(args[i]) == 0)
contradicts the limits imposed by parseArgs that disallows empty strings in the array. More importantly, args[i] may be the sentinel NULL value, and strlen expects a non-NULL pointer to a valid string.
This termination condition should simply check if args[i] is NULL.
With
strcpy(inputFilePath, args[i+1]);
args[i+1] might also be the NULL sentinel value, and strcpy also expects non-NULL pointers to valid strings. You can see this in action when inputSymbol is a match for the final token in the array.
args[i+1] may also evaluate as args[MAX_ARGS], which would be out of bounds.
Additionally, inputFilePath has a string length limit of MAX_CHAR - 1, and args[i+1] is (possibly!) a dynamically allocated string whose length might exceed this.
Some edge cases found in getInput:
Both arguments to
strcat(loc_prompt, PROMPT);
are of the size MAX_CHAR. Since loc_prompt has a length of 1. If PROMPT has the length MAX_CHAR - 1, the resulting string will have the length MAX_CHAR. This would leave no room for the NUL terminating byte.
readline can return NULL in some situations, so
buf = readline(loc_prompt);
if (strlen(buf) != 0) {
can again pass the NULL pointer to strlen.
A similar issue as before, on success readline returns a string of dynamic length, and
strcpy(input, buf);
can cause a buffer overflow by attempting to copy a string greater in length than MAX_CHAR - 1.
buf is a pointer to data allocated by malloc. It's unclear what add_history does, but this pointer must eventually be passed to free.
Some considerations.
Firstly, it is a good habit to initialize your data, even if it might not matter.
Secondly, using constants (#define MAX_CHAR 256) might help to reduce magic numbers, but they can lead you to design your program too rigidly if used in the same way.
Consider building your functions to accept a limit as an argument, and return a length. This allows you to more strictly track the sizes of your data, and prevents you from always designing around the maximum potential case.
A slightly contrived example of designing like this. We can see that find does not have to concern itself with possibly checking MAX_ARGS elements, as it is told precisely how long the list of valid elements is.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_ARGS 100
char *get_input(char *dest, size_t sz, const char *display) {
char *res;
if (display)
printf("%s", display);
if ((res = fgets(dest, sz, stdin)))
dest[strcspn(dest, "\n")] = '\0';
return res;
}
size_t find(char **list, size_t length, const char *str) {
for (size_t i = 0; i < length; i++)
if (strcmp(list[i], str) == 0)
return i;
return length;
}
size_t split(char **list, size_t limit, char *source, const char *delim) {
size_t length = 0;
char *token;
while (length < limit && (token = strsep(&source, delim)))
if (*token)
list[length++] = token;
return length;
}
int main(void) {
char input[512] = { 0 };
char *args[MAX_ARGS] = { 0 };
puts("Welcome to the shell.");
while (1) {
if (get_input(input, sizeof input, "$ ")) {
size_t argl = split(args, MAX_ARGS, input, " ");
size_t redirection = find(args, argl, "<");
puts("Command parts:");
for (size_t i = 0; i < redirection; i++)
printf("%zu: %s\n", i, args[i]);
puts("Input files:");
if (redirection == argl)
puts("[[NONE]]");
else for (size_t i = redirection + 1; i < argl; i++)
printf("%zu: %s\n", i, args[i]);
}
}
}

What is the purpose of this clear_mem function?

I've been trying to work out exactly what this function's purpose is I've come across..
The code intentionally has bad code practices, so I am trying to figure out if this is one of them.
Here is the function:
void clear_mem(char *memblock, int siz) {
register int i;
for (i=0; i<=siz;i++)
*(memblock+i) = 0;
}
The function is called within the following function:
char *get_argument(char line[], int argno){
char *argument = malloc(512);
char clone[512];
strncpy(clone, line, strlen(line)+1);
int current_arg = 0;
char *splitted = strtok(clone, " ");
while (splitted != NULL){
if (splitted[0] != ':'){
current_arg++;
}
if (current_arg == argno+1){
clear_mem(argument, 512); //Here
strncpy(argument, splitted, strlen(splitted)+1);
return argument;
free(argument);
}
splitted = strtok(NULL, " ");
}
if (current_arg != argno){
argument[0] = '\0';
}
free(argument);
return argument;
}
Thanks in advance!
In this code:
for (i=0; i<=siz;i++)
*(memblock+i) = 0;
memblock+i adds the integer i to the pointer memblock. The result points i elements beyond where memblock points. Since memblock is a pointer to char, the result points i characters beyond where memblock points.
Then *(memblock+i) refers to the character at that address. *(memblock+i) is equivalent to memblock[i]. *(memblock+i) = 0 sets the character to zero.
So the effect of this code is to set all characters indexed by i during the loop to zero. It clears a block of memory.
The for (i=0; i<=siz;i++) causes the loop to iterate with i taking all values from zero up to and including siz. Thus, siz+1 characters will be set to zero.
We can see this is an error because get_argument allocates 512 bytes for argument and then later calls clear_mem(argument, 512), which clears 513 bytes. The resulting behavior is not defined by the C code.

String Allocated with malloc Enaccessible after Function Return

I have run into a strange bug and I cannot for the life of me get it figured out. I have a function that decodes a byte array into a string based on another encoding function. The function that decodes looks roughly like this:
char *decode_string( uint8_t *encoded_string, uint32_t length,
uint8_t encoding_bits ) {
char *sequence_string;
uint32_t idx = 0;
uint32_t posn_in_buffer;
uint32_t posn_in_cell;
uint32_t encoded_nucleotide;
uint32_t bit_mask;
// Useful Constants
const uint8_t CELL_SIZE = 8;
const uint8_t NUCL_PER_CELL = CELL_SIZE / encoding_bits;
sequence_string = malloc( sizeof(char) * (length + 1) );
if ( !sequence_string ) {
ERR_PRINT("could not allocate enough space to decode the string\n");
return NULL;
}
// Iterate over the buffer, converting one nucleotide at a time.
while ( idx < length ) {
posn_in_buffer = idx / NUCL_PER_CELL;
posn_in_cell = idx % NUCL_PER_CELL;
encoded_nucleotide = encoded_string[posn_in_buffer];
encoded_nucleotide >>= (CELL_SIZE - encoding_bits*(posn_in_cell+1));
bit_mask = (1 << encoding_bits) - 1;
encoded_nucleotide &= bit_mask;
sequence_string[idx] = decode_nucleotide( encoded_nucleotide );
// decode_nucleotide returns a char on integer input.
idx++;
}
sequence_string[idx] = '\0';
printf("%s", sequence_string); // prints the correct string
return sequence_string;
}
The bug is that the return pointer, if I try to print it, causes a segmentation fault. But calling printf("%s\n", sequence_string) inside of the function will print everything just fine. If I call the function like this:
const char *seq = "AA";
uint8_t *encoded_seq;
encode_string( &encoded_seq, seq, 2, 2);
char *decoded_seq = decode_string( encoded_seq, 2, 2);
if ( decoded_seq ) {
printf("%s\n",decoded_seq); // this crashes
if ( !strcmp(decoded_seq, seq) ) {
printf("Success!");
}
then it will crash on the print.
A few notes, the other functions seem to all work, I've tested them fairly thoroughly (i.e. decode_nucleotide, encode_string). The string also prints correctly inside the function. It is only after the function returns that it stops working.
My question is, what might cause this memory to become invalid just by returning the pointer from a function? Thanks in advance!
First (and not that important, but) in the statement:
sequence_string = malloc( sizeof(char) * (length + 1) );
sizeof(char) by definition is always == 1. so the statement becomes:
sequence_string = malloc(length + 1);
In this section of your post:
char *decoded_seq = decode_string( encoded_seq, 2, 2);
...since I cannot see your implementation of decode_string, I can only make assumptions about how you are verifying its output before returning it. I do however understand that you are expecting the return value to contain values that would be legal contents for a C string. I can also assume that because you are working with coding and decoding, that the output type is likely unsigned char. If I am correct, then a legal range of characters for an output type of unsigned char is 0-255.
You are not checking the output before sending the value to the printf statement. If the value at the memory address of decoded_seq happens to be 0, (in the range of unsigned char) your program would crash. String functions do not work well with null pointers.
You should verify the return of _decode_string_ sending it to printf
char *decoded_seq = decode_string( encoded_seq, 2, 2);
if(decoded_seq != NULL)
{
...

C Get complete words from first 15 characters of string

I have a function that will return either the first 13 characters of a string or the second 13 characters of a string:
char* get_headsign_text(char* string, int position) {
if (position == 1){
char* myString = malloc(13);
strncpy(myString, string, 13);
myString[13] = '\0'; //null terminate destination
return myString;
free(myString);
} else {
char* myString = malloc(13);
string += 13;
strncpy(myString, string, 13);
myString[13] = '\0'; //null terminate destination
return myString;
free(myString);
}
}
I would like to have it so that the function will return only complete words (not cutoff words in the middle).
Example:
If the string is "Hi I'm Christopher"
get_headsign_text(string, 1) = "Hi I'm "
get_headsign_text(string, 2) = "Christopher"
So if the function would have cut within a word, instead it would cut before that last word, and if so, if it is trying to get the second 13 it would include the word that would have been cut.
When taking various edge cases into consideration, the structure of the code needs to change considerably.
For instance:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
inline int min_int(int a, int b) {
return a < b ? a : b;
}
inline int is_word_char(char c) {
return isgraph(c);
}
char* get_headsign_text(char* string, int position) {
int start_index, end_index;
if (position == 1) {
start_index = 0;
} else {
start_index = 13;
}
end_index = min_int(strlen(string) + 1, start_index + 13);
start_index = min_int(start_index, end_index);
int was_word_char = 1;
while(start_index > 0 && (was_word_char = is_word_char(string[start_index]))) {
--start_index;
}
if(!was_word_char) {
++start_index;
}
while(end_index > start_index && is_word_char(string[end_index])) {
--end_index;
}
int myStringLen = end_index - start_index;
char *myString = malloc(myStringLen + 1);
strncpy(myString, string + start_index, myStringLen);
myString[myStringLen] = '\0';
return myString;
}
int main(void) {
char s[] = "Hi, I\'m Christopher";
char *r1 = get_headsign_text(s, 1);
char *r2 = get_headsign_text(s, 2);
printf("<%s>\n<%s>\n", r1, r2);
free(r1);
free(r2);
return 0;
}
That said, there are numerous other problems/concerns with the code snippet you posted:
In the assignment myString[13] = '\0';, you are assigning to memory which you have not allocated. Although you have allocated 13 bytes, myString[13] refers to one byte past the last allocated byte.
Nothing after the return statement gets executed, and the calls to free are never reached.
You shouldn't be returning a block of memory only to free it immediately! It's quite counter-productive to give something to the caller only to take it away. :)
You do not validate the size of the string. Unless you are absolutely certain this will only be called on strings of sufficient length, your function will segfault when, say, position is 2 and your string buffer is only, say, 10 bytes long.
You need to check if your last character is not a blank space '' then it should find for trailing space and cut your string to that index.
Keep track of your spaces using an index variable. If the character count is 13, and the current character is not a space or null terminator, then adjust your character count by subtracting that and the last space index. Save the string, and then continue from the last space index.
You have a multitude of issues.
First issue, you're free'ing after returning the myString - meaning this function will not free the string.
Second issue. You allocate 13 chars, then you set the 13rd char to null. Are you sure this does what you expect?
Third issue - why are you adding 13 to the string pointer? What should that do?
Lastly, you should think about which character is used to separate words - when you've figured which one that is, try to scan for it and cut your string where it is.

Return the contiguous block in c

I create an array (char *charheap;) of length 32 bytes in the heap, and initialize all the elements to be \0. Here is my main function:
int main(void) {
char *str1 = alloc_and_print(5, "hello");
char *str2 = alloc_and_print(5, "brian");
}
char *alloc_and_print(int s, const char *cpy) {
char *ncb = char_alloc(s);// allocate the next contiguous block
if (ret == NULL) {
printf("Failed\n");
} else {
strcpy(ncb, cpy);
arr_print();// print the array
}
return ncb;
}
Here is what I implement:
/char_alloc(s): find the FIRST contiguous block of s+1 NULL ('\0')
characters in charheap that does not contain the NULL terminator
of some previously allocated string./
char *char_alloc(int s) {
int len = strlen(charheap);
for (int i = 0; i < len; i++) {
if (charheap[0] == '\0') {
char a = charheap[0];
return &a;
} else if (charheap[i] == '\0') {
char b = charheap[i+1];
return &b;
}
}
return NULL;
}
Expected Output: (\ means \0)
hello\\\\\\\\\\\\\\\\\\\\\\\\\\\
hello\brian\\\\\\\\\\\\\\\\\\\\\
This solution is completely wrong and I just print out two failed. :(
Actually, the char_alloc should return a pointer to the start of contiguous block but I don't know how to implement it properly. Can someone give me a hint or clue ?
Your function is returning a pointer to a local variable, therefore the caller receives a pointer to invalid memory. Just return the pointer into the charheap, which is what you want.
return &charheap[0]; /* was return &a; which is wrong */
return &charheap[i+1]; /* was return &b; which is wrong */
Your for loop uses i < len for the terminating condition, but, since charheap is \0 filled, strlen() will return a size of 0. You want to iterate through the whole charheap, so just use the size of that array (32 in this case).
int len = 32; /* or sizeof(charheap) if it is declared as an array */
The above two fixes should be enough to get your program to behave as you expect (see demonstration).
However, you do not place a check to make sure there is enough room in your heap to accept the allocation check. Your allocation should fail if the distance between the start of the available memory and the end of the charheap is less than or equal to the desired size. You can enforce this easily enough by setting the len to be the last point you are willing to check before you know there will not be enough space.
int len = 32 - s;
Finally, when you try to allocate a third string, your loop will skip over the first allocated string, but will overwrite the second allocated string. Your loop logic needs to change to skip over each allocated string. You first check if the current location in your charheap is free or not. If it is not, you advance your position by the length of the string, plus one more to skip over the '\0' terminator for the string. If the current location is free, you return it. If you are not able to find a free location, you return NULL.
char *char_alloc(int s) {
int i = 0;
int len = 32 - s;
while (i < len) {
if (charheap[i] == '\0') return &charheap[i];
i += strlen(charheap+i) + 1;
}
return NULL;
}

Resources