Variadic Arguments Overflow on Embedded Target - c

I am writing a library for interfacing with the HD44780U LCD driver on a TM4C123GH6PM.
I have the following function
void lcd_printf(LCD *lcd, const char *format, ...) {
char str[81];
int length;
va_list args;
va_start(args, format);
length = vsnprintf(str, sizeof(str), format, args);
va_end(args);
for (int i = 0; i < length; ++i) {
if (str[i] == '\0') {
break;
}
lcd_putc(lcd, str[i]);
}
}
which, when run as follows
int main() {
// omitted init code
lcd_printf(&lcd, "%i", 1);
}
causes an hardware fault, I believe due to a buffer overflow. The error is somewhat intermittent, the same line is used in various places in the code and only some cause errors. If %i is not used and instead a plain string is printed, no error occurs. Unfortunately, the more robust vsnprintf_s is not available with my toolchain.
If I write out the equivalent code with snprintf in the same location, like so
int main() {
// omitted init code
char str[81];
int length;
length = snprintf(str, 81, "%i", 1);
for (int i = 0; i < length; ++i) {
if (str[i] == '\0') {
break;
}
lcd_putc(lcd, str[i]);
}
}
Then the code runs without any problems.
What errors have I made in lcd_printf which invite this overflow?

Related

How to Tokenize String without using strtok()

I'm trying to tokenize a string without using a strtok().
When I run characters of string, it will print in each line.
For instance, when I run:
printfTokens("Hello from other side!");
The output should be:
Hello
from
other
side!
As I'm just learning C, I'm stuck for hours on how to implement this program. So far, I only know the basics and playing around with not (still haven't learned any calloc, malloc, etc).
So far I have this code, but the output does not print anything.
#include <stdio.h>
#include <string.h>
#define MAX_WORD 100
void printfTokens(char *inputString) {
int i;
/*int inputStringLength;
for(i = 0; inputString[i] != '/0'; i++) {
inputStringLength++;
}*/
while(inputString[i] != '\0') {
char testing[MAX_WORD];
while(inputString[i] != ' ') {
testing[inputString[i]]++;
i++;
}
printf("%s", testing);
i++;
}
}
int main() {
printfTokens("TESTING ONE! TWO! THREE!");
return 0;
}
You do not initialize the variable i.
while(inputString[i] != '\0') can be written while(inputString[i]).
testing[inputString[i]]++ makes sense to count the number of occurrences of a given character from inputString, but it does not make sense to print it. You may want to do something like:
while(1)
{
char testing[MAX_WORD], *t=testing;
while(inputString[i]&&(inputString[i]!=' '))
*t++=inputString[i++];
if (t>testing) printf("%s", testing);
if (!inputString[i]) break;
i++;
}
It would be better to name MAX_WORD_LENGTH instead of MAX_WORD.
These are a few problems in your code.
Sample tokenization function.
size_t tokenize(const char *inputString, const char *delim, char **argv, size_t maxtokens)
{
size_t ntokens = 0;
char *tokenized = strdup(inputString);
if(tokenized)
{
argv[0] = tokenized;
while(*tokenized)
{
if(strchr(delim, *tokenized))
{
*tokenized = 0;
ntokens++;
if(ntokens == maxtokens - 1)
{
break;
}
argv[ntokens] = tokenized + 1;
}
tokenized++;
}
}
return ntokens + 1;
}
int main()
{
char *tokens[10];
size_t ntokens = tokenize("TESTING ONE! TWO! THREE!", " ", tokens , 10);
for(size_t i = 0; i < ntokens; i++)
{
printf("Token[%zu] = `%s`\n", i, tokens[i]);
}
free(tokens[0]);
return 0;
}
https://godbolt.org/z/znv8PszG6

Why doesn't the code run with a test.in file created by Sublime TextEditor

This was a piece of code I have written for my assignment, some of the weird code design are not controllable by me. I am currently writing these on MacOS.
file1
#include <stdio.h>
extern int read_palindrome();
int main()
{
if (read_palindrome()) printf("input is a palindrome");
else printf("input is not a palindrome");
return 0;
}
file2
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int check_palindrome2(char *, int);
// malloc() will be used as usual to set aside an initial memory
// The entire input will be read gradually by characters using getchar()
// In the event we require more memory than what we have previously,
// use realloc() to increase memory size dynamically
int read_palindrome() {
unsigned int len_max = 128;
unsigned int current_size = 0;
char *pStr = malloc(len_max);
current_size = len_max;
int i = 0;
int c = EOF;
if (pStr == NULL) {
return -1;
}
while (( c = getchar() ) != '\n') {
pStr[i] = (char)c;
i++;
if(i == current_size) {
current_size += len_max;
char *tmp = realloc(pStr, current_size);
if (tmp == NULL) {
free(pStr);
return -1;
}
pStr = tmp;
}
}
int retval = check_palindrome2(pStr,i);
free(pStr);
return retval;
}
int check_palindrome2(char *s, int length) {
for (int i = 0; i < length / 2; i++) {
if (s[i] != s[length-i-1])
return 0;
}
return 1;
}
I would think this code works except for empty files, which will cause my program to continuously expect input and not terminate. However, I realised when using Sublime Text, creating a test.in file without pressing "Enter" somehow displays the "non-terminating" behaviour as well, while typing something in vim without pressing "Enter" for a newline still allows the code to work. Does anyone know the reason behind this phenomenon?

removing a char from a string in c by index

how can I remove a certain char from a string in c without using any library
functions? the function i wrote seems to have a run time error and i can not figure out why.
void remove_char(char* s,int index)
{
while(s[index+1] != 0)
{
s[index] = s[index + 1];
index++;
}
s[index] = 0;
}
I was also wondering if there is a way to remove a char in a complexity of 1?
Without seeing how you are calling your function, knowing the exact failure mode is difficult. Calling it with char string[] = "this is a string";, it worked fine. But as the comments suggest, some other forms of input strings may cause a problem.
I have used the following implementation with no problems. It removes all occurrences of a specified character:
int RemoveCharFromString(char *orig, char c, char *newStr)
{
if(!orig) return -1;
if(!newStr) return -1;
int i=0, j=0;
while (*(orig+i) != '\0')
{
if (*(orig+i) != c)
{
*(newStr+j) = *(orig+i);
j++;
i++;
}
else i++;
}
*(newStr+j) = '\0';
return 0;
}
Or, as you requested, this one removes a character at a specified index:
int RemoveCharFromStringByIndex(char *orig, int index, char *newStr)
{
if(!orig) return -1;
if(!newStr) return -1;
int i=0, j=0;
while (*(orig+i) != '\0')
{
if (i != index)
{
*(newStr+j) = *(orig+i);
j++;
i++;
}
else i++;
}
*(newStr+j) = '\0';
return 0;
}
Notice, in this implementation newStr is created by caller and must include space enough to contain the result.
You can certainly adapt these in any way you need to. An improvement would be to change the prototype to:
char * RemoveCharFromString(char *orig, char c);
Or
char * RemoveCharFromStringByIndex(char *orig, int index)
But that will be something you can do, if you wish it so.
I always like this for removing a specific char from a string:
int RemoveCharFromString(const char *src, char *dst, char c)
{
const char *s;
char *d;
if ((char *)0 != src && (char *)0 != dst) {
for (d = dst, s = src; (*d = *s); s++) {
if (c != *d)
d++;
}
return 0;
}
return 1;
}
Only increment the destination pointer if it's not equal to the character being skipped. Sometimes you see this using the passed arguments src and dst directly, but way back when some compilers would produce more efficient code with separate pointers. The "const" simply allows you to pass a constant string as the source without producing a compiler error.

C code to read config file and parse directives

I'm trying to read a config file and parse the config directives. So far I have the following code, I need advice on how to improve this or change it. Is this efficient? Thanks!
struct config
{
char host;
char port;
}
void parse_line(char *buf) {
char *line;
if(strstr(buf, "host=") || strstr(buf, "host = ") || strstr(buf, "host= ") || strstr(buf, "host =")) {
line = strstr(buf, "=");
printf("Host: %s", &line[2]);
} else if(strstr(buf, "port=") || strstr(buf, "port = ") || strstr(buf, "port= ") || strstr(buf, "port =")) {
line = strstr(buf, "=");
printf("Port: %s", &line[2]);
}
}
int main(int argc, char *argv[])
{
char *file_name;
FILE *file;
file_name = argv[1];
file = fopen(file_name, "r");
// check if file is NULL, etc..
char buffer[BUFSIZ];
char *line;
int i;
while(fgets(buffer, sizeof(buffer), file) != NULL) {
for(i = 0; i < strlen(buffer); i++) { // iterate through the chars in a line
if(buffer[i] == '#') { // if char is a #, stop processing chars on this line
break;
} else if(buffer[i] == ' ') { // if char is whitespace, continue until something is found
continue;
} else {
parse_line(buffer); // if char is not a # and not whitespace, it is a config directive, parse it
break;
}
}
}
fclose(file);
return 0;
}
I am looking for a way to ignore # if it is a first character on a line, and also lines that are white spaces. I think my code does that, but is that efficient?
EDIT:
Thanks everyone for all the suggestions, I have managed to do this simple code to trim the white spaces, so that I wouldn't need all the strstr() calls.
void trim(char *src)
{
int i, len;
len = strlen(src);
for(i = 0; i < len; i++) {
if(src[i] == ' ') {
continue;
}
if(src[i] == '\n' || src[i] == '#') {
break;
}
printf("%c", src[i]); // prints: host=1.2.3.4
}
}
int main(void)
{
char *str = "host = 1.2.3.4 # this is a comment\n";
trim(str);
return EXIT_SUCCESS;
}
It prints correctly: host=1.2.3.4 but now I need this in a variable to be further parsed. I think I will try to use strcpy.
EDIT 2:
I do not think that strcpy is the right choice. Those chars are printed out in a loop, so every time I use strcpy, the previous char is overwritten. I have tried this, but it does not work because only the host= part is placed into arr. The IP part is not placed into arr.. how can this be fixed..
char arr[sizeof(src)];
for(i = 0; i < len; i++) {
if(src[i] == ' ') {
continue;
}
if(src[i] == '\n' || src[i] == '#') {
break;
}
printf("%c", src[i]); // prints: host=1.2.3.4
arr[i] = src[i];
}
int j;
for(j = 0; j < sizeof(arr); j++) {
printf("%c", arr[j]); //prints: host=
}
EDIT 3:
I found the correct way of placing chars into arr:
int i, count = 0;
for(i = 0; i < len; i++) {
if(src[i] == ' ') {
continue;
}
if(src[i] == '\n' || src[i] == '#') {
break;
}
arr[count] = src[i];
count++;
}
Your implementation is pretty fragile. Parsers really ought to verify syntax and return errors when they see something unexpected. For example, yours should detect missing fields and multiply defined ones.
Fortunately this parsing problem is simple enough for sscanf to handle everything:
skip blank lines,
skip comments
ignore any amount of whitespace
extract the key/value pairs
Here's code:
#include <stdio.h>
#define CONFIG_SIZE (256)
#define HOST_SET (1)
#define PORT_SET (2)
typedef struct config {
unsigned set;
char host[CONFIG_SIZE];
unsigned long port;
} CONFIG;
// Parse the buffer for config info. Return an error code or 0 for no error.
int parse_config(char *buf, CONFIG *config) {
char dummy[CONFIG_SIZE];
if (sscanf(buf, " %s", dummy) == EOF) return 0; // blank line
if (sscanf(buf, " %[#]", dummy) == 1) return 0; // comment
if (sscanf(buf, " host = %s", config->host) == 1) {
if (config->set & HOST_SET) return HOST_SET; // error; host already set
config->set |= HOST_SET;
return 0;
}
if (sscanf(buf, " port = %lu", &config->port) == 1) {
if (config->set & PORT_SET) return PORT_SET; // error; port already set
config->set |= PORT_SET;
return 0;
}
return 3; // syntax error
}
void init_config(CONFIG *config) {
config->set = 0u;
}
void print_config(CONFIG *config) {
printf("[host=%s,port=", config->set & HOST_SET ? config->host : "<unset>");
if (config->set & PORT_SET) printf("%lu]", config->port); else printf("<unset>]");
}
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s CONFIG_FILE\n", argv[0]);
return 1;
}
FILE *f = fopen(argv[1], "r");
char buf[CONFIG_SIZE];
CONFIG config[1];
init_config(config);
int line_number = 0;
while (fgets(buf, sizeof buf, f)) {
++line_number;
int err = parse_config(buf, config);
if (err) fprintf(stderr, "error line %d: %d\n", line_number, err);
}
print_config(config);
return 0;
}
With this input:
# This is a comment
This isn't
# Non-leading comment
host = 123.456.789.10
###
port =42
port= 1
host=fruit.foo.bar
the output is
error line 3: 3
error line 10: 2
error line 11: 1
[host=fruit.foo.bar,port=1]
Note that when the parser discovers a field has already been set, it still uses the latest value in the config. It's easy enough to keep the original instead. I'll let you have that fun.
I think parse_line is a little bit rigid for my taste, I would use strtok
instead. Then you don't have to worry too much about spaces, like you do if you
have a space before the = sign.
Your struct is also wrong, host and port would only hold a character.
Besides port should be an integer. And you need a semicolon ; after the
struct definition.
struct config
{
char host[100];
int port;
};
int parse_line(struct config *config, char *buf)
{
if(config == NULL || buf == NULL)
return 0;
char varname[100];
char value[100];
const char* sep = "=\n"; // get also rid of newlines
char *token;
token = strtok(buf, sep);
strncpy(varname, token, sizeof varname);
varname[sizeof(varname) - 1] = 0; // making sure that varname is C-String
trim(varname);
token = strtok(NULL, sep);
if(token == NULL)
{
// line not in format var=val
return 0;
}
strncpy(value, token, sizeof value);
value[sizeof(varname) - 1] = 0
trim(value);
if(strcmp(varname, "port") == 0)
{
config->port = atoi(value);
return 1;
}
if(strcmp(varname, "host") == 0)
{
strncpy(config->host, value, siezof config->host);
config->host[(sizeof config->host) - 1] = 0;
return 1;
}
// var=val not recognized
return 0;
}
Note that I used a function called trim. This function is not part of the
standard library. Below I posted a possible implementation of such a function.
I like using trim because it gets rid of white spaces. Now you can do this in
main:
struct config config;
// initializing
config.port = 0;
config.host[0] = 0;
int linecnt = 0;
while(fgets(buffer, sizeof(buffer), file) != NULL) {
linecnt++;
trim(buffer);
if(buffer[0] == '#')
continue;
if(!parse_line(&config, buffer))
{
fprintf(stderr, "Error on line %d, ignoring.\n", linecnt);
continue;
}
}
A possible implementation of trim
void rtrim(char *src)
{
size_t i, len;
volatile int isblank = 1;
if(src == NULL) return;
len = strlen(src);
if(len == 0) return;
for(i = len - 1; i > 0; i--)
{
isblank = isspace(src[i]);
if(isblank)
src[i] = 0;
else
break;
}
if(isspace(src[i]))
src[i] = 0;
}
void ltrim(char *src)
{
size_t i, len;
if(src == NULL) return;
i = 0;
len = strlen(src);
if(len == 0) return;
while(src[i] && isspace(src[i]))
i++;
memmove(src, src + i, len - i + 1);
return;
}
void trim(char *src)
{
rtrim(src);
ltrim(src);
}
There are a few ways that you can improve performance:
Calling strstr() in this scenario is inefficient, because the presence of the "host" part of buf can be checked once instead of multiple times every time strstr() is called. Instead, make an if statement that checks if buf begins with "host", then check if buf contains the other elements. The same thing applies to the portion of code checking for the presence of "port".
In the loop in main, instead of doing this:
for(i = 0; i < strlen(buffer); i++) { // iterate through the chars in a line
if(buffer[i] == '#') { // if char is a #, stop processing chars on this line
break;
} else if(buffer[i] == ' ') { // if char is whitespace, continue until something is found
continue;
} else {
parse_line(buffer); // if char is not a # and not whitespace, it is a config directive, parse it
break;
}
do this:
for(i = 0; i < strlen(buffer); i++) { // iterate through the chars in a line
char temp = buffer[i];
if(temp == '#') { // if char is a #, stop processing chars on this line
break;
} else if (temp != ' ') {
parse_line(buffer); // if char is not a # and not whitespace, it is a config directive, parse it
break;
}
Checking to see if something is not equal to another is likely to be just as fast as checking if they are equal (at least on Intel, the je (jump equal) and jne (jump not equal) instructions exhibit the same latency of 1 cycle each), so the statement with the continue in it is not necessary. The temp variable is so that buffer[i] does not need to be calculated in the second if again in case the first if is false. Also, do what user3121023 stated below (same reason for performance as creating the temp variable).
You can use operating-system-specific functions (such as thos from the library WINAPI/WIN32/WIN64 (synonyms) on windows) instead of C standard library functions. Microsoft has very good documentation about their functions in the MSDN (Microsoft Developer Network) web site.
Use uint_fast8_t (defined in stdint.h, this typedef is set to the fastest integer type greater than or equal to the size in bits specified in the typedef) when performing operations on the host and port (but use chars when storing the variables on the disk, in order to make read i/o operations faster).
This isn't related to performance , but use return EXIT_SUCCESS; in main instead of return 0;, since using EXIT_SUCCESS is more readable and exhibits the same performance.
Honestly, I can't help but wonder if rolling your own parser is so great.
Why not use an existing JSON or YAML parser and test for keys in the parsed data?
This will be easily extendible by allowing for new keys to be added with very little effort and the common format of the configuration file makes it very easy for developers to edit.
If you are going to roll out your own parser, than some of the previously mentioned advice makes a lot of sense.
The biggest ones are: don't seek the whole buffer, read the single line that's in front of you and report any errors. Also, advance as you go.
Your parser should work correctly if someone would dump a GigaByte of garbage into the configuration file, so make no assumptions about the data.

Returning a string to a variable length character array

I have a program that reverses a string from an input of a variable length character array. The function returns a variable length character array and is printed. When I print the output, I do get the reversed string, but there are garbage characters appended to it in my console print.
Is this a "legal" operation in terms of returning to buffers? Can someone please critique my code and suggest a better alternative if it is not the right approach?
Thanks.
#include <stdio.h>
#include <stdlib.h>
char *reverse_string(char *input_string);
char *reverse_string(char *input_string)
{
int i=0;
int j=0;
char *return_string;
char filled_buffer[16];
while (input_string[i]!='\0')
i++;
while (i!=0)
{
filled_buffer[j]=input_string[i-1];
i--;
j++;
}
return_string=filled_buffer;
printf("%s", return_string);
return return_string;
}
int main (void)
{
char *returned_string;
returned_string=reverse_string("tasdflkj");
printf("%s", returned_string);
return 1;
}
This is my output from Xcode - jklfdsat\347\322̲\227\377\231\235
No, it isn't safe to return a pointer to a local string in a function. C won't stop you doing it (though sometimes the compiler will warn you if you ask it to; in this case, the local variable return_string prevents it giving the warning unless you change the code to return filled_buffer;). But it is not safe. Basically, the space gets reused by other functions, and so they merrily trample on what was once a neatly formatted string.
Can you explain this comment in more detail — "No, it isn't safe..."
The local variables (as opposed to string constants) go out of scope when the function returns. Returning a pointer to an out-of-scope variable is undefined behaviour, which is something to be avoided at all costs. When you invoke undefined behaviour, anything can happen — including the program appearing to work — and there are no grounds for complaint, even if the program reformats your hard drive. Further, it is not guaranteed that the same thing will happen on different machines, or even with different versions of the same compiler on your current machine.
Either pass the output buffer to the function, or have the function use malloc() to allocate memory which can be returned to and freed by the calling function.
Pass output buffer to function
#include <stdio.h>
#include <string.h>
int reverse_string(char *input_string, char *buffer, size_t bufsiz);
int reverse_string(char *input_string, char *buffer, size_t bufsiz)
{
size_t j = 0;
size_t i = strlen(input_string);
if (i >= bufsiz)
return -1;
buffer[i] = '\0';
while (i != 0)
{
buffer[j] = input_string[i-1];
i--;
j++;
}
printf("%s\n", buffer);
return 0;
}
int main (void)
{
char buffer[16];
if (reverse_string("tasdflkj", buffer, sizeof(buffer)) == 0)
printf("%s\n", buffer);
return 0;
}
Memory allocation
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *reverse_string(char *input_string);
char *reverse_string(char *input_string)
{
size_t j = 0;
size_t i = strlen(input_string) + 1;
char *string = malloc(i);
if (string != 0)
{
string[--i] = '\0';
while (i != 0)
{
string[j] = input_string[i-1];
i--;
j++;
}
printf("%s\n", string);
}
return string;
}
int main (void)
{
char *buffer = reverse_string("tasdflkj");
if (buffer != 0)
{
printf("%s\n", buffer);
free(buffer);
}
return 0;
}
Note that the sample code includes a newline at the end of each format string; it makes it easier to tell where the ends of the strings are.
This is an alternative main() which shows that the allocated memory returned is OK even after multiple calls to the reverse_string() function (which was modified to take a const char * instead of a plain char * argument, but was otherwise unchanged).
int main (void)
{
const char *strings[4] =
{
"tasdflkj",
"amanaplanacanalpanama",
"tajikistan",
"ablewasiereisawelba",
};
char *reverse[4];
for (int i = 0; i < 4; i++)
{
reverse[i] = reverse_string(strings[i]);
if (reverse[i] != 0)
printf("[%s] reversed [%s]\n", strings[i], reverse[i]);
}
for (int i = 0; i < 4; i++)
{
printf("Still valid: %s\n", reverse[i]);
free(reverse[i]);
}
return 0;
}
Also (as pwny pointed out in his answer before I added this note to mine), you need to make sure your string is null terminated. It still isn't safe to return a pointer to the local string, even though you might not immediately spot the problem with your sample code. This accounts for the garbage at the end of your output.
First, returning a pointer to a local like that isn't safe. The idiom is to receive a pointer to a large enough buffer as a parameter to the function and fill it with the result.
The garbage is probably because you're not null-terminating your result string. Make sure you append '\0' at the end.
EDIT: This is one way you could write your function using idiomatic C.
//buffer must be >= string_length + 1
void reverse_string(char *input_string, char* buffer, size_t string_length)
{
int i = string_length;
int j = 0;
while (i != 0)
{
buffer[j] = input_string[i-1];
i--;
j++;
}
buffer[j] = '\0'; //null-terminate the string
printf("%s", buffer);
}
Then, you call it somewhat like:
#define MAX_LENGTH 16
int main()
{
char* foo = "foo";
size_t length = strlen(foo);
char buffer[MAX_LENGTH];
if(length < MAX_LENGTH)
{
reverse_string(foo, buffer, length);
printf("%s", buffer);
}
else
{
printf("Error, string to reverse is too long");
}
}

Resources