File descriptors in linux; Pwnable kr challenge [duplicate] - c

This question already has answers here:
Writing to stdin and reading from stdout (UNIX/LINUX/C Programming)
(5 answers)
Closed 10 months ago.
In order to understand the workings of C and linux, I was doing the pwnable.kr challenges, but there is something in the first challenge that doesn't quite add to me.
#include <stdlib.h>
#include <string.h>
char buf[32];
int main(int argc, char* argv[], char* envp[]){
if(argc<2){
printf("pass argv[1] a number\n");
return 0;
}
int fd = atoi( argv[1] ) - 0x1234;
int len = 0;
len = read(fd, buf, 32);
if(!strcmp("LETMEWIN\n", buf)){
printf("good job :)\n");
system("/bin/cat flag");
exit(0);
}
printf("learn about Linux file IO\n");
return 0;
}
To my understanding, the 0 fd is for stdin, and the 1 and 2 fd are for stdout, yet all three of them allow me to solve the challenge. I understand the first one, but wouldn't the other 2 file descriptors just read what I print on my promt, and not allow me to write anything after?

Let's take a look at what /proc says:
$ ls -al /proc/self/fd
total 0
dr-x------. 2 mrsam mrsam 0 Apr 23 15:25 .
dr-xr-xr-x. 9 mrsam mrsam 0 Apr 23 15:25 ..
lrwx------. 1 mrsam mrsam 64 Apr 23 15:25 0 -> /dev/pts/1
lrwx------. 1 mrsam mrsam 64 Apr 23 15:25 1 -> /dev/pts/1
lrwx------. 1 mrsam mrsam 64 Apr 23 15:25 2 -> /dev/pts/1
For an interactive terminal all three file descriptors are, basically the same. All three are connected to the terminal device, after all, why won't they be? Input and output goes to the same screen.
And, for simplicity, you can think of them as dup()s of each other. So, as a side effect, all three file descriptors are read/write.
Now, of course, this is only true for programs launched from an interactive terminal. In an arbitrary program, launched in an unknown environment you can only rely on being able to read from 0 and write to 1 and 2.

Related

Why 20 is omitted from the file descriptor

EDIT:
lsof shows that it's opened by ptmx. Thanks to #zwol
My code below prints the file descriptor returned from open. I noticed that 20 is missing. There is no similar question to the best of my knowledge.
background:
Filesystem: ext4
Ubuntu 20.04 on WSL2
code:
int main()
{
char name[2] = "a";
for (int i = 0; i < 52; i++) {
int fd = open(name, O_RDWR | O_CREAT, 0644);
printf("fd is %d\n", fd);
}
return 0;
}
output:
$ ./a.out
fd is 3
fd is 4
fd is 5
...
fd is 18
fd is 19
fd is 21 <-- here
fd is 22
...
lsof
...
a.out 1815 ryan 19u REG 8,16 0 42321 /tmp/tmp/a
a.out 1815 ryan 20u CHR 5,2 0t0 15832 /dev/ptmx
a.out 1815 ryan 21u REG 8,16 0 42321 /tmp/tmp/a
...
I have 2 questions:
What's the reason behind it? (What triggers ptmx)
Will there be more indices missing if I keep opening files? (More programs like ptmx?)
POSIX says that every system call that allocates file descriptors must use the lowest number(s) that are not already in use. Therefore, descriptor number 20 must have already been open. Your sample program doesn't open anything before the loop, so it must have been inherited from your shell, or opened by the C library prior to main.
You can find out more by having your program print its PID and then sleep for a long time, after the loop, and then running lsof on it while it's sleeping.

Realize a console in a GTK3 GUI programming in C

I realized a GUI with GTK3 that, essentially, generates an input text file for an exe program that with these inputs can do elaborations.
This exe is put in executions in the GUI by mean of a System call ( system("exe input.dat &") ).
This exe can print on screen message of information or error.
What I want to do is redirect these message on a GtkTextView.
The idea that I had is to redirect output and error on a file ( system("exe input.dat > output_file.txt 2>&1 &") ) and in the GUI read line by line this
file and send this strings in the textView.
I was not sure that 2 process can write and read the same file and to test this concept I used these 2 simple programs:
the writer (used like ./writer > out_file.txt):
#include <stdio.h>
#include <unistd.h>
main()
{
int a;
while(1)
{
fprintf(stdout,"a=%d\n",a);
fflush(stdout);
sleep(1);
a++;
}
}
and the reader:
#include <stdio.h>
#include <string.h>
int main()
{
FILE *fp;
fp = fopen("out_file.txt","r");
char string_new[1024];
char string_old[1024];
strcpy(string_old," ");
while(1)
{
fgets(string_new,1024,fp);
if ( strlen(string_new) != 0 )
{
if ( strcmp(string_new, string_old) != 0 )
{
fprintf(stdout,"%s",string_new);
fflush(stdout);
strcpy(string_old,string_new);
}
}
}
}
This two programs run correctly and the second one print the output of the first one.
Putting in the GUI a similar code, the GUI read only the first line of the file.
How I can solve this issue?
Thank you
You should use popen instead of executing system("exe input.dat &"), then it's easy to read from the stdout output of the program.
Like this:
#include <stdio.h>
int main(void)
{
FILE *fp = popen("ls -lah /tmp", "r");
if(fp == NULL)
return 1;
char buffer[1024];
int linecnt = 0;
while(fgets(buffer, sizeof buffer, fp))
printf("Line: %d: %s", ++linecnt, buffer);
putchar('\n');
fclose(fp);
return 0;
}
which outputs:
$ ./b
Line: 1: total 108K
Line: 2: drwxrwxrwt 8 root root 12K Mar 10 02:30 .
Line: 3: drwxr-xr-x 26 root root 4.0K Feb 15 01:05 ..
Line: 4: -rwxr-xr-x 1 shaoran shaoran 16K Mar 9 22:29 a
Line: 5: -rw-r--r-- 1 shaoran shaoran 3.6K Mar 9 22:29 a.c
Line: 6: -rw------- 1 shaoran shaoran 16K Mar 9 22:29 .a.c.swp
Line: 7: -rwxr-xr-x 1 shaoran shaoran 11K Mar 10 02:30 b
Line: 8: -rw-r--r-- 1 shaoran shaoran 274 Mar 10 02:30 b.c
Line: 9: -rw------- 1 shaoran shaoran 12K Mar 10 02:30 .b.c.swp
Line: 10: drwx------ 2 shaoran shaoran 4.0K Mar 9 20:08 firefox_shaoran
Line: 11: drwxrwxrwt 2 root root 4.0K Mar 9 20:06 .ICE-unix
Line: 12: srwx------ 1 mongodb mongodb 0 Mar 9 20:07 mongodb-27017.sock
Line: 13: prwx------ 1 shaoran shaoran 0 Mar 9 20:08 oaucipc-c2s-1874
Line: 14: prwx------ 1 shaoran shaoran 0 Mar 9 20:08 oaucipc-s2c-1874
Line: 15: drwxrwxr-x 2 root utmp 4.0K Mar 9 20:06 screen
Line: 16: drwx------ 2 shaoran shaoran 4.0K Mar 9 20:07 ssh-XueH0w8zWCSE
Line: 17: drwx------ 2 shaoran shaoran 4.0K Mar 9 20:08 thunderbird_shaoran
Line: 18: -r--r--r-- 1 root root 11 Mar 9 20:07 .X0-lock
Line: 19: drwxrwxrwt 2 root root 4.0K Mar 9 20:07 .X11-unix
If you need more control and want also to read stderr, then you would have to create pipes for stdout and stderr,
make a fork and the child dup2 the pipes to stderr & stdout and
then execute exec (or any other function of that family) to execute the
program.
Like this:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void)
{
int stdout_pipe[2];
int stderr_pipe[2];
pipe(stdout_pipe);
pipe(stderr_pipe);
pid_t pid = fork();
if(pid < 0)
return 1;
if(pid == 0)
{
// closing reading ends and duplicating writing ends
close(stdout_pipe[0]);
dup2(stdout_pipe[1], STDOUT_FILENO);
close(stderr_pipe[0]);
dup2(stderr_pipe[1], STDERR_FILENO);
execlp("ls", "ls", "-alh", "a.c", "kslkdl", NULL);
exit(1);
}
// closing writing end
close(stdout_pipe[1]);
close(stderr_pipe[1]);
int status;
if(waitpid(pid, &status, 0) < 0)
{
fprintf(stderr, "could not wait\n");
return 1;
}
if(WIFEXITED(status) == 0)
{
fprintf(stderr, "ls exited abnormally\n");
close(stdout_pipe[0]);
close(stderr_pipe[0]);
return 1;
}
puts("STDOUT:");
char buffer[1024];
ssize_t len;
while((len = read(stdout_pipe[0], buffer, sizeof(buffer) - 1)) > 0)
{
buffer[len] = 0;
printf("%s", buffer);
}
putchar('\n');
close(stdout_pipe[0]);
puts("STDERR:");
while((len = read(stderr_pipe[0], buffer, sizeof(buffer) - 1)) > 0)
{
buffer[len] = 0;
printf("%s", buffer);
}
putchar('\n');
close(stderr_pipe[0]);
return 0;
}
which outputs:
$ ./b
STDOUT:
-rw-r--r-- 1 shaoran shaoran 3.6K Mar 9 22:29 a.c
STDERR:
ls: cannot access 'kslkdl': No such file or directory
Pablo's answer is correct, you need to use pipe(7)-s.
And you could probably use GTK & Glib's g_spawn_async_with_pipes (which is based on pipe and fork and execve on Linux) for that (instead of fork or popen). In a GTK interactive program, it is better than the usual popen because the forked program would run concurrently with your event loop.
You could even consider using g_source_add_unix_fd on the (or on some) of the pipe fd-s given by pipe(2) or by g_spawn_async_with_pipes which use that pipe(2) call. But you might prefer g_io_channel_unix_new and g_io_add_watch
Be aware that the GTK main loop (and Gtk Input and Event Handling Model), i.e. GtkApplication and the related g_application_run or the older gtk_main are an event loop around some multiplexing system call like poll(2) (or the older select(2)) and you probably need that loop to be aware of your pipes. When some data arrives on the pipe, you probably want to read(2) it (and then call some GtkTextBuffer insert function).
You should make design choices: do you want the GUI interface and the other process to run concurrently? Or is the other exe process always so quick and with a small output (and no input) that you might just use popen?
On current GUI applications, the event loop should run quickly (at least 30 or 50 times per second) if you want a responsive GUI app.
Look also for inspiration inside the source code of some existing free software GTK application (e.g. on github or from your Linux distro).

how many inodes, open file table entries and file descriptors this program use

I am reviewing some stuff for my job interview, on this question it asks at peak how many inodes, open file table entries and file descriptors this program use? Can you help me with it?
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char* argv[]){
char buffer[3] = "ab";
int r = open("new.txt", O_RDONLY);
int r1, r2, pid;
r1 = dup(r);
read(r, buffer, 1);
if((pid=fork())==0) {
r1 = open("new.txt", O_RDONLY);
} else{
waitpid(pid, NULL, 0);
}
read(r1, buffer+1, 1);
printf("%s", buffer);
return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char* argv[]){
char buffer[3] = "ab";
int r = open("new.txt", O_RDONLY);
int r1, r2, pid;
r1 = dup(r);
read(r, buffer, 1);
if((pid=fork())==0) {
r1 = open("new.txt", O_RDONLY);
while (1)
{
sleep(1);
}
} else{
waitpid(pid, NULL, 0);
while (1)
{
sleep(1);
}
}
read(r1, buffer+1, 1);
printf("%s", buffer);
return 0;
}
wutiejun#linux-00343520:~/Temp> gcc -o test main.c
wutiejun#linux-00343520:~/Temp> ./test &
[1] 10404
wutiejun#linux-00343520:~/Temp> ls -l /proc/10404/fd
Total 0
lrwx------ 1 wutiejun users 64 June 9 16:33 0 -> /dev/pts/0
lrwx------ 1 wutiejun users 64 June 9 16:33 1 -> /dev/pts/0
lrwx------ 1 wutiejun users 64 June 9 16:33 2 -> /dev/pts/0
l-wx------ 1 wutiejun users 64 June 9 16:33 3 -> /home/wutiejun/Temp/new.txt
l-wx------ 1 wutiejun users 64 June 9 16:33 4 -> /home/wutiejun/Temp/new.txt
wutiejun#linux-00343520:~/Temp> ps -a
PID TTY TIME CMD
10404 pts/0 00:00:00 test
10405 pts/0 00:00:00 test
10417 pts/0 00:00:00 ps
wutiejun#linux-00343520:~/Temp> ls -l /proc/10405/fd
Total 0
lrwx------ 1 wutiejun users 64 June 9 16:34 0 -> /dev/pts/0
lrwx------ 1 wutiejun users 64 June 9 16:34 1 -> /dev/pts/0
lrwx------ 1 wutiejun users 64 June 9 16:33 2 -> /dev/pts/0
lr-x------ 1 wutiejun users 64 June 9 16:34 3 -> /home/wutiejun/Temp/new.txt
lr-x------ 1 wutiejun users 64 June 9 16:34 4 -> /home/wutiejun/Temp/new.txt
lr-x------ 1 wutiejun users 64 June 9 16:33 5 -> /home/wutiejun/Temp/new.txt
wutiejun#linux-00343520:~/Temp>
So, you can count the open files.
And I am not sure about the inode numbers, I think it depends on the different filesystems.
We have to make a few assumptions here.
Assumption 1: We are running on a system where all filesystems use inodes (it's just a name of a data structure. Inodes have not been exposed as an abstraction to processes for decades). A perfectly valid answer for the "number of inodes" question could be: 0 because I'm running on NFS. I would be much more comfortable if the term used was "vnode" because even though it's also a system specific term it is actually more meaningful in many more systems than "inode".
Assumption 2: the program starts with stdin/stderr/stdout open and pointing to the same file description.
Assumption 3: stdin&co are a pty from /dev
Assumption 4: /dev is a normal directory, as in classic unix and not like linux where it's a devfs. Also, the pty was opened as a normal file just like in the early 90s, not through some kind of cloning mechanism.
Assumption 5: "new.txt" exists and is accessible to this process.
Assumption 6: "file table entry" actually means file description.
Assumption 7: the program is not dynamically linked. This is because a dynamic linker could have opened an arbitrarily large number of files before the program reached main.
Assumption 8: stdio in libc doesn't open various locale and such databases until they are actually needed.
Assumption 9: things that can fail (fork, open, dup), don't.
Assumption 10: pid_t fits in an int.
Let's see what happens (the skipped lines are irrelevant to the question).
int main(int argc, char* argv[]){
At this point we have three open file descriptors. As in the assumptions, those file descriptors point to the same file description which comes from the same file on the filesystem.
Score (descriptors, descriptions, inodes): 3, 1, 1
int r = open("new.txt", O_RDONLY);
We successfully open a file.
Score: 4, 2, 2
r1 = dup(r);
We successfully dup the file descriptor. A dup:d file descriptor points to the same file description as the original descriptor.
Score: 5, 2, 2
if((pid=fork())==0) {
Fork copies the file descriptor table, but the descriptors still point to the same descriptions.
Score: 10, 2, 2
r1 = open("new.txt", O_RDONLY);
This creates a new file description that points to the same inode as we've already opened.
Score: 11, 3, 2
} else {
waitpid(pid, NULL, 0);
}
Let's assume that the child process has successfully exited despite doing it in a quite unclean way (forked children should exit through _exit, not by returning from main, but we'll let it slide this time). The child process closes all its descriptors, this also causes one description to be closed because it only had one reference.
Score: 5, 2, 2
Answer: Given all the assumptions, the peak was 11 file descriptors, 3 file descriptions ("file table entries" if we insist on using archaic terminology), 2 inodes.
Alternative answer: 11, 2 + X, 1 + Y, where X is the number of descriptions that stdin/out/err use and Y is the same for number of inodes.

How can I take as input, the output from another c file? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
So for example, let say I have a file server.c which is not printing anything but has a string e.g: "Fish is swimming in the air". What I want to do is to let child.c to print the string of server.c
Is that even possible? I was told that using pipelines (such popen()) would help. But I can't find what I want out there.
I'm sure there is possibly a way to do that using pipe functions (check something like this site unixwiz.net/techtips/remap-pip-fds.html) but what you are describing sounds like another client connecting to the server and having the strings sent to it across a socket. Using a socket would also open up the ability to check the server string over a network. Typically with error/extra log checking with a server it is either handled by the server opening a log file or by sending it across a socket. You could determine to send it to a specific client that uses PSK over a TLS connection if security is an issue.
For TLS examples check out https://github.com/wolfSSL/wolfssl-examples
Added in code for piping
receiver.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4
5 int main() {
6
7 char buffer[1024];
8
9 fscanf(stdin, "%s", buffer);
10
11 printf("receiver got data and is printing it\n");
12 printf("%s\n", buffer);
13
14 return 0;
15 }
sender.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4
5
6 int main()
7 {
8 FILE *output;
9
10 output = popen ("./receiver","w");
11 if (!output) {
12 /* error checking opening pipe */
13 fprintf(stderr, "could not open pipe\n");
14 return 1;
15 }
16
17 fprintf(output, "%s", "hello_world\n");
18
19 if (pclose (output) != 0) {
20 /* error checking on closing pipe */
21 fprintf(stderr, " could not run receiver\n");
22 return 1;
23 }
24
25 return 0;
26 }
compile and run in the same directory using
gcc sender.c -o sender
gcc receiver.c -o receiver
./sender

Error while reading a file after creat system call

I am creating a file in read/write mode and writing a string into it. Then I am trying to read it into a buffer where I get read error.
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd,count,fd1;
char buf[10];
fd=creat("./smarak",S_IRWXU);
if(fd<0)
{
perror("creat");
}
count=write(fd,"Hello smarak",7);
printf("count=%d\n",count);
count=read(fd,buf,7);
printf("buf=%s\n",buf);
printf("%d\n",count);
}
I get nothing in buf and also count is -1 which is read error. Why this error? Isn't it possible to read a file created by creat() system call?
You need to reposition between writing and reading:
count=write(fd,"Hello smarak",7);
printf("count=%d\n",count);
// added:
if ( lseek( fd, 0, SEEK_SET ) < 0 )
{
perror("lseek");
}
count=read(fd,buf,7);
printf("buf=%s\n",buf);
printf("%d\n",count);
After your write, the current position in the file is right after what you have written. If you want to read that back in, you have to "rewind" the current position to the beginning of the file.
Check man lseek for details.
And I don't know how Unix calls handle this, but the C standard (C99, 7.19.5.3 The fopen function, section 6) has this to say:
[...] output shall not be directly followed by input without an
intervening call to the fflush function or to a file positioning
function (fseek, fsetpos, or rewind), and input shall not be directly
followed by output without an intervening call to a file positioning
function, unless the input operation encounters end-of-file.
So you might be looking at undefined behaviour in your code sample.
use lseek() function to set the position to the specified.
Try to use open() system call instead of creat() system call.
When you use creat() it will open the process as
root#Lenovo-G585:/proc/6988/fd$ ls -la
total 0
dr-x------ 2 root root 0 Sep 18 16:20 .
dr-xr-xr-x 8 root root 0 Sep 18 16:20 ..
lrwx------ 1 root root 64 Sep 18 16:20 0 -> /dev/pts/4
lrwx------ 1 root root 64 Sep 18 16:20 1 -> /dev/pts/4
lrwx------ 1 root root 64 Sep 18 16:20 2 -> /dev/pts/4
l-wx------ 1 root root 64 Sep 18 16:20 3 -> /tmp/smarak
^
Look ^ here.
The read permission is missing so you cannot able to read from that file.
If you use open() system call like
fd=open("./smarak",O_CREAT|O_RDWR);
O_CREAT - Which is used to create a new file if doesn't exist.
O_RDWR - Which is used to open a file for read and write mode.
By using open with the given argument, you can do your requirement.
When using creat() it will open the file in O_CREAT|O_WRONLY|O_TRUNC.
O_TRUNC - Which is used to truncate remove the file content and keep
the cursor position in start of file.
Note: While using creat() it will truncate the file if already exist.

Resources