I have usb char device which I managed to bind to /dev/device0 with usb skeleton 2.2 driver (only with few comments to understand it).
Now I have to write user application, which will send and recieve commands as ascii chars.
I am able to send commands with write without problems, but I don't know how to read properly from device.
As I don't know how long the message will be, I tried something like this
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char** argv)
{
char *c, *ret;
int fd,err;
ret = malloc(1);
char *dev = "/dev/device0";
fd = open(dev, O_RDWR);
printf("fd: %d\n", fd);
if (fd == -1) {
printf("fopen() failed");
exit(1);
}
command = "command1";
write(fd, command, strlen(command));
while (read(fd, ret,1)!=EOF)
{
fprintf(stderr,"%c\n",ret);
}
close(fd);
return 0;
}
but it doesn't work, it seems to deadlock somehow or get into state very similar to that. I was able to find, that the number of reads is random, usually 3-6 and then the program waits (maybe waiting for data from device, but I'm not sure with this), during last read function wait_event_interruptible() in driver's read() function returns -512 and meanwhile the callback function of penultimate read isn't called.
Why is this happening and how do I find out whether there are any data the device sent?
char *ret;
Memory is not allocated to your pointer ret and you are writing to that location which is UB.Hence you might see a crash. Allocate memory to your pointer.
Edits:
Else
If you just want to reach character by character have
char ret;
read():
read returns the number of characters it read. When it reaches the end
of the file, it won't be able to read any more (at all) and it'll
return 0, not EOF.
So make the below changes:
while (read(fd, ret,1)!= 0)
{
fprintf(stderr,"%c\n",ret);
}
Related
I am trying to create a simple program using pipes, even though that are easier options for the same task (fopen(), lseek(), ftell(), etc).
First I use execve() to perform a terminal cat, and send the information through the pipe so I may be able to print the size of the file descriptor and read it to a malloc'd char pointer. My solution is this one:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <fcntl.h>
int main (int argc, char *argv[]) {
int fd[2], active;
int pipe_sz = 0;
char *name;
pipe(fd);
//change /bin/cat according to your system
char *cmd[] = {"/bin/cat", "example.txt", NULL};
if (fork() == 0) {
//CHILD
dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
active = execve(cmd[0], cmd, NULL);
} else {
//PARENT
wait(NULL);
int ioctl_sz = ioctl(fd[0], FIONREAD, &pipe_sz);
name = malloc(pipe_sz + 1);
int result = read(fd[0], name, pipe_sz);
name[pipe_sz] = '\0';
printf("LEN NAME: %ld\n", strlen(name));
close(fd[0]);
close(fd[1]);
}
return 0;
}
Everything works fine as long as I keep myself inside the pipe's limits (my LINUX system is able to hold 65536 bytes). I decided to push the limits, and see what could happen. As expected, the program stays stuck when my file was above 65536 bytes.
I do not want to manually change my pipe's limits through fcntl(pipefd, F_SETPIPE_SZ, size) (since I "don't know" my file's size). Therefore, I researched, and came across the pipe2(pipefd, O_NONBLOCK) in order to avoid my pipe from stopping the reading process. Unfortunately, I received only this message:
/bin/cat: write error: Resource temporarily unavailable
I even tried a while loop read() and realloc() to see if my code could at least give the char pointer result, but I was not successful, and part of file was lost.
Is it possible to produce a code that may provide the same results as mine above with files bigger than 65536 bytes?
Is there a way to interrupt the pipe if it takes too long to finish the process? Or even make a while loop to guess and resize the pipe through
fcntl()?
Thanks everyone for the help!
I'm trying to use posix_openpt on Mac. The issue I'm seeing is that I get a file descriptor back from posix_openpt. I use the file descriptor for reading and create a copy using dup for writing. The issue I'm running into is that when I write to the master file descriptor, I read that data back out from the master. So no data ends up at the slave. I confirmed this by using posix_spawnp to run a program with stdin/stdout/stderr set to the slave file. The program hangs indefinitely waiting for input. Here is my code (note, all error handling was removed for legibility):
int master_fd = posix_openpt(O_RDWR);
grantpt(master_fd);
unlockpt(master_fd);
char *slave_filename_orig = ptsname(master_fd);
size_t slave_filename_len = strlen(slave_filename_orig);
char slave_filename[slave_filename_len + 1];
strcpy(slave_filename, slave_filename_orig);
posix_spawn_file_actions_t fd_actions;
posix_spawn_file_actions_init(&fd_actions);
posix_spawn_file_actions_addopen(&fd_actions, STDIN_FILENO, slave_filename, O_RDONLY, 0644);
posix_spawn_file_actions_addopen(&fd_actions, STDOUT_FILENO, slave_filename, O_WRONLY, 0644);
posix_spawn_file_actions_adddup2(&fd_actions, STDOUT_FILENO, STDERR_FILENO);
pid_t pid;
posix_spawnp(&pid, "wc", &fd_actions, NULL, NULL, NULL);
int master_fd_write = dup(master_fd);
char *data = "hello world";
write(master_fd_write, data, strlen(data));
close(master_fd_write);
char buffer[1024];
read(master_fd, buffer, 1024); // <- Issue Here
// buffer now contains hello world. It should contain the output of `wc`
(Note: The above was only tested on Linux; I don't have a Mac to work on, but I have no reason to believe it's any different in the details here.)
There are several problems with your code:
At least on Linux, calling posix_spawn() with a null pointer causes a crash. You need to provide all the arguments. Even if Macs accept it the way you have it, doing this is a Good Idea.
Next, wc reading from standard input will wait until an attempt to read more data gives an End Of File condition before it prints out the statistics it gathers; your code doesn't do this. With a pty, if you write a specific byte (Typically with the value 4, but it can be different, so best to use what the terminal says instead of hardcoding it) to it, the terminal driver will recognize that as signalling EOF without having to close the master like you would when using a pipe (Making it impossible to read the output of wc).
Second, the terminal's default settings include echoing the input; that's what you're reading.
A cleaned up version that addresses these issues and more (Like yours, with most error checking omitted; real code should be checking all these functions for errors):
#define _XOPEN_SOURCE 700
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <spawn.h>
#include <termios.h>
#include <unistd.h>
#include <wait.h>
int main(void) {
int master_fd = posix_openpt(O_RDWR);
grantpt(master_fd);
unlockpt(master_fd);
char *slave_filename_orig = ptsname(master_fd);
size_t slave_filename_len = strlen(slave_filename_orig);
char slave_filename[slave_filename_len + 1];
strcpy(slave_filename, slave_filename_orig);
//printf("slave pty filename: %s\n", slave_filename);
// Open the slave pty in this process
int slave_fd = open(slave_filename, O_RDWR);
// Set up slave pty to not echo input
struct termios tty_attrs;
tcgetattr(slave_fd, &tty_attrs);
tty_attrs.c_lflag &= ~ECHO;
tcsetattr(slave_fd, TCSANOW, &tty_attrs);
posix_spawn_file_actions_t fd_actions;
posix_spawn_file_actions_init(&fd_actions);
// Use adddup2 instead of addopen since we already have the pty open.
posix_spawn_file_actions_adddup2(&fd_actions, slave_fd, STDIN_FILENO);
posix_spawn_file_actions_adddup2(&fd_actions, slave_fd, STDOUT_FILENO);
// Also close the master and original slave fd in the child
posix_spawn_file_actions_addclose(&fd_actions, master_fd);
posix_spawn_file_actions_addclose(&fd_actions, slave_fd);
posix_spawnattr_t attrs;
posix_spawnattr_init(&attrs);
pid_t pid;
extern char **environ;
char *const spawn_argv[] = {"wc" , NULL};
posix_spawnp(&pid, "wc", &fd_actions, &attrs, spawn_argv, environ);
close(slave_fd); // No longer needed in the parent process
const char *data = "hello world\n";
ssize_t len = strlen(data);
if (write(master_fd, data, len) != len) {
perror("write");
}
// Send the terminal's end of file interrupt
cc_t tty_eof = tty_attrs.c_cc[VEOF];
if (write(master_fd, &tty_eof, sizeof tty_eof) != sizeof tty_eof) {
perror("write EOF");
}
// Wait for wc to exit
int status;
waitpid(pid, &status, 0);
char buffer[1024];
ssize_t bytes = read(master_fd, buffer, 1024);
if (bytes > 0) {
fwrite(buffer, 1, bytes, stdout);
}
close(master_fd);
return 0;
}
When compiled and run, outputs
1 2 12
There are two problems with this code.
First, you are seeing "hello world" on master_fd because by default terminals echo. You need to set the terminal to raw mode to suppress that.
Second, wc won't output anything until it sees an EOF, and it will not see an EOF until you close the master. Not just master_fd_write mind you, but all copies of master_fd, including master_fd itself. However, once you close the master, you cannot read from it.
Choose some other program that wc to demonstrate the functionality of posix_openpt.
Edit: It is possible to raise the end-of-file condition on the slave without closing the master by writing ^D (EOT, ascii 4).
I wrote this piece of code to show the basic working of how I would like to send some data (Strings) from the parent process to the child process. But I seem to have some problems. (I removed all error checking to make the code more readable)
When I run this piece of code I expect to see the two test strings to be displayed on the terminal, but I only see the first one.
When I uncomment the first “sleep(1)”, then I see both strings displayed.
But when I uncomment only the second “sleep(1)”, then I again only see the first string.
I suspect this problem has something to do with synchronization. That the strings get written to fast and the fifo write end closes before everything is read by the child process. That’s why we see the correct output when we introduce a sleep between the two write() commands.
But what I don’t understand is that we still get a faulty output when we only introduce a sleep after both write commands. Why can’t the child read both strings even if they are both written before it can read one?
How can I solve this problem? Do I need some synchronization code, and if so how should I implement this. Because I won’t write a “sleep(1)” after every write command.
And is the solution also viable for multiple processes that want to write to the same fifo? (but with still only one process that reads from the fifo)
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char const *argv[]) {
mkfifo("test_fifo", 0666);
pid_t pid = fork();
if (pid == 0) {
int fd = open("test_fifo", O_RDONLY);
char data[400];
int rc;
do {
rc = read(fd, data, 400);
if (rc > 0) printf("Received data: %s\n", data);
} while(rc > 0);
}
else {
int fd = open("test_fifo", O_WRONLY);
char * string1 = "This is the first test string";
write(fd, string1, strlen(string1) + 1);
//sleep(1);
char * string2 = "This is the second test string";
write(fd, string2, strlen(string2) + 1);
//sleep(1);
close(fd);
wait(NULL);
}
return 0;
}
You are receiving both strings at the same time at the first call to read. Because %s prints up until a zero byte, the second string is just not displayed. The poor mans synchronization with sleep(1) allows child to "catch" the messages in two distinct read call.
read returns the count of bytes read. Use that number. Change the parent code to:
ssize_t rc;
do {
rc = read(fd, data, 400);
if (rc > 0) {
printf("Received data: ");
for (size_t i = 0; i < rc; ++i) {
if (data[i] == '\0') {
printf("\\x00");
continue;
}
printf("%c", data[i]);
}
printf("\n");
}
} while(rc >= 0);
and it shows on my pc:
Received data: This is the first test string\x00This is the second test string\x00
Why can’t the child read both strings even if they are both written before it can read one?
Well, the problem is not in reading, it's how you are displaying the data you read. (Still, reading could be improved, one should handle that pesky EAGAIN errno code).
How can I solve this problem?
If you want 1:1 relationship between read/write use a constant size packets or generally you have to know in advance how many bytes you want to read. Bytes written are "concatenated" together and lose structure. Or use pipe(3p) on which messages with size smaller then PIPE_BUF are guaranteed to be atomic. Or you could use POSIX message queue mq_receive/mq_send.
Or write a proper "deserializer", something that will buffer data and keep internal state and notify higher level only when a whole "message" was received, ie. detect when a zero byte was received in the stream of bytes and restore structure the the stream of bytes.
I'm working on linux, I have a file that contains a line like this:
328abc
I would like, in C, to read the integer part (328) and the characters 'a','b','c', using only the function:
ssize_t read (int filedes, void *buffer, size_t size))
This is the only thing the file contains.
I know there are better ways to do that with other functions, but I haven't coded in C for a long time, and trying to help a friend, only this function is alowed.
How do I play with the buffer to do that?
Thanks
edit:
I understand that I need to parse the buffer manually. and my question is how?
If that's the only thing in the file. This will do:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main() {
char buffer[6];
char intBuffer[4];
ssize_t bytesRead;
int number;
int fd;
if ((fd = open("file.txt", O_RDONLY)) == -1) {
perror("Error opening file");
exit(EXIT_FAILURE);
}
if ((bytesRead = read(fd, buffer, 6)) == -1) {
perror("Error reading file");
exit(EXIT_FAILURE);
}
memcpy(intBuffer, buffer, 3);
intBuffer[3] = '\0';
number = atoi(intBuffer);
printf("The number is %d\n", number);
exit(EXIT_SUCCESS);
}
The following code will print "The number is 328".
Is this some kind of homework?
I am asking because there are better ways to do that than using the read function.
Anyway to answer your question, read reads size bytes from the file whose file descriptor is filedes and places them to the buffer.
It does not know anything about line breaks etc. So you need to manually find where a line ends, etc. If you want to only use read, then you need to manually parse the buffer after each call to read (supposing your files contains many lines, that you want to parse).
Beware that a line may be split between two read calls, so you need to handle that case with caution.
I see that the code below uses memcpy which i can use to exploit this program and cause a buffer overflow, but i cant seem to make it crash. No matter what character argument i pass to it i just get "error opening packet file." Any ideas how?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#define MAX_ADDR_LEN 128
#define ADDR_LENGTH_OFFSET 4
#define ADDR_OFFSET 8
typedef unsigned char shsize_t;
typedef struct{
char addr[MAX_ADDR_LEN];
shsize_t len;
} arp_addr;
void
print_address(char *packet)
{
arp_addr hwaddr;
int i;
hwaddr.len = (shsize_t) *(packet + ADDR_LENGTH_OFFSET);
memcpy(hwaddr.addr, packet + ADDR_OFFSET, hwaddr.len);
printf("Sender hardware address: ");
for (i = 0; i < hwaddr.len - 1; i ++)
printf("%02hhx::", hwaddr.addr[i]);
printf("%02hhx\n", hwaddr.addr[hwaddr.len - 1]);
return;
}
int main(int argc, char *argv[])
{
struct stat sbuf;
char *packet;
int fd;
if (argc != 2){
printf("Usage: %s <packet file>\n", argv[0]);
return EXIT_FAILURE;
}
if ((stat(argv[1], &sbuf)) < 0){
printf("Error opening packet file\n");
return EXIT_FAILURE;
}
if ((fd = open(argv[1], O_RDONLY)) < 0){
printf("Error opening packet file\n");
return EXIT_FAILURE;
}
if ((packet = (char *)malloc(sbuf.st_size * sizeof(char))) == NULL){
printf("Error allocating memory\n");
return EXIT_FAILURE;
}
if (read(fd, packet, sbuf.st_size) < 0){
printf("Error reading packet from file\n");
return EXIT_FAILURE;
}
close(fd);
print_address(packet);
free(packet);
return EXIT_SUCCESS;
}
When you do something like write past the end of a buffer there is no guarantee that the program will crash. This is called undefined behavior, which literally means that you can make no reasonable assumptions as to what will happen.
The program itself appears relatively well behaved. As long as len is calculated properly I don't see any way for you to cause an overrun via input. Just because a program uses memcpy doesn't mean that it is vulnerable to attack. The only attack vector I see is if you pass it a carefully crafted file such that the length is calculated incorrectly:
hwaddr.len = (shsize_t) *(packet + ADDR_LENGTH_OFFSET)
In this line the program reads ADDR_LENGTH_OFFSET bytes from the address of packet to get the data length. Obviously that is problematic if you craft a file with an erroneous value for the data length in the header (i.e., a data length > MAX_ADDR_LEN).
BTW, the argument is a file, not a character. You won't be able to do anything passing it nonsense input because read will fail.
No matter what character argument i pass to it i just get "error
opening packet file."
You need to pass a valid file name as an argument, not random characters.
As others have indicated, the memcpy() isn't the security problem. The problem is that the length parameter passed to memcpy() comes from user input (the file you specified). If you specify a file that has a length field of, say, a billion, you will probably see a crash (and, yes, 'crash' is accepted vernacular).
Since there is rather limited checking on the size of the packet, you can pass it the name of an empty or very short file and the print_address() code will mess around out of bounds.
Also, since the code reads a length from the data read from the file, you can place an arbitrary number at relevant position and make the code go running around most places in memory.