User avatar
HermannSW
Posts: 4667
Joined: Fri Jul 22, 2016 9:09 pm
Location: Eberbach, Germany
Contact: Website Twitter YouTube

memfd_create system call for bash

Thu Sep 23, 2021 7:16 pm

I posted on memrun.c which creates a file that lives in RAM using memfd_create system call, copies ELF binary created by gcc into it and executes it -- no auxiliary file from gcc gets stored in filesystem that way, is like tcc's "-run" option:
viewtopic.php?f=33&t=319473#p1915302

While memrun.c is fine, it does three things:
  1. create memory file
  2. copy compiled executable to memory file (from pipe or process substitution)
  3. execute memory file
2 and 3 can be done in bash or on command line. New memfd_create.c just exposes memfd_create system call for use with bash. After creating memory file, it creates the proc name for accessing the memory file and prints it to the calling process. That way bash script gets name for using the memory file. Memory file lives as long as memfd_create is running (it sleeps while waiting to be terminated). Bash script needs to extract pid from file name and just kill that pid, then memory file will not be accessible anymore:
https://github.com/Hermann-SW/memrun/tr ... e-for-bash
memfd_create.c.html.crop.png
memfd_create.c.html.crop.png
memfd_create.c.html.crop.png (30.19 KiB) Viewed 1720 times

This is output of demo script utilizing memory file:

Code: Select all

pi@raspberrypi400:~/memrun/C $ ./memfd_create4bash 
/proc/15167/fd/3
foo
bar
cat: /proc/15167/fd/3: No such file or directory
pi@raspberrypi400:~/memrun/C $ 

This is memfd_create4bash:
memfd_create4bash.html.crop.png
memfd_create4bash.html.crop.png
memfd_create4bash.html.crop.png (28.32 KiB) Viewed 1720 times

So in case you need a file located in RAM, that should not exists in filesystem, then memfd_create.c as demonstrated is for you.


P.S:
"-run" option enabled gcc and g++ now make use of memfd_create.c, see this posting for details:
viewtopic.php?f=33&t=319919&p=1916996#p1916996
From "git diff" for bin/grun:
Image
https://github.com/Hermann-SW/memrun
https://stamm-wilbrandt.de/2wheel_balancing_robot
https://stamm-wilbrandt.de/en#raspcatbot
https://github.com/Hermann-SW/Raspberry_v1_camera_global_external_shutter
https://stamm-wilbrandt.de/en/Raspberry_camera.html

trejan
Posts: 3736
Joined: Tue Jul 02, 2019 2:28 pm

Re: memfd_create system call for bash

Thu Sep 23, 2021 7:58 pm

Erm. Your C as a scripting language adventures are getting weirder and weirder. I'm not going to debate usefulness of this.

If you do want to do this then mktemp with /dev/shm is easier IMO. /dev/shm is a tmpfs so nothing is stored onto disk and mktemp will create a unique directory in it. Default permissions will be 0700 unless you've changed your umask.

Don't change it to create a temporary file directly in /dev/shm and use that as the output for gcc as there will be a potential race condition due to gcc unlinking the file first.

Code: Select all

#!/bin/bash
export TMPDIR=`mktemp -d -p /dev/shm`
gcc $1 -o $TMPDIR/prog
$TMPDIR/prog
rm -rf $TMPDIR
The variable name is significant as gcc will create temporary files in /tmp if you don't give it a TMPDIR location to use instead. gcc with the -pipe option also works but doesn't do the object files. TMPDIR is better as it will cover any other programs that honour it when creating temporary files.

User avatar
HermannSW
Posts: 4667
Joined: Fri Jul 22, 2016 9:09 pm
Location: Eberbach, Germany
Contact: Website Twitter YouTube

Re: memfd_create system call for bash

Fri Sep 24, 2021 6:57 am

Thanks, I investigated gcc/g++ temporary files.
I did set TMPDIR and did endless loop outputting that directory.
Empty at start and at end, but in between there are temporary file:

Code: Select all

-rw------- 1 pi pi    0 Sep 24 08:47 ccAUFETI.ld
-rw------- 1 pi pi    0 Sep 24 08:47 ccEM7aKl.c
-rw------- 1 pi pi    0 Sep 24 08:47 ccOCUutU.le
-rw------- 1 pi pi    0 Sep 24 08:47 cct9ygUh.res
-rw------- 1 pi pi 0 Sep 24 08:47 ccTkBKka.o
-rw------- 1 pi pi    0 Sep 24 08:47 ccY7ETjx.o
-rw------- 1 pi pi 2456 Sep 24 08:47 ccTkBKka.o
This is regardless of "-pipe" or not.

Next I did set TMPDIR to "/dev/null".
strace revealed that it used "/tmp" then.
After "sudo mv /tmp /tmp.sic" strace revealed that "/var/tmp" was used.
After "sudo mv /var/tmp /var/tmp.sic" strace revealed that "/usr/tmp" was tried, but does not exists -- finally g++ used local directory for temp files.

Now I created a directory, removed write access for user, group and others and tried to compile in that directory then.
With that setup g++ gave up and aborted:

Code: Select all

pi@raspberrypi400:~/memrun/C/tmp $ g++ -pipe ../demo.cpp -o ../demo 2>../err
Aborted
pi@raspberrypi400:~/memrun/C/tmp $ 

strace gives more details:

Code: Select all

pi@raspberrypi400:~/memrun/C/tmp $ strace g++ -pipe ../demo.cpp -o ../demo 2>../err
Aborted
pi@raspberrypi400:~/memrun/C/tmp $ tail -11 ../err
openat(AT_FDCWD, "/usr/share/locale/en/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
write(2, "Cannot create temporary file in "..., 54Cannot create temporary file in ./: Permission denied
) = 54
rt_sigprocmask(SIG_UNBLOCK, [ABRT], NULL, 8) = 0
rt_sigprocmask(SIG_BLOCK, ~[RTMIN RT_1], [], 8) = 0
getpid()                                = 23255
gettid()                                = 23255
tgkill(23255, 23255, SIGABRT)           = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
--- SIGABRT {si_signo=SIGABRT, si_code=SI_TKILL, si_pid=23255, si_uid=1000} ---
+++ killed by SIGABRT +++
pi@raspberrypi400:~/memrun/C/tmp $ 

So as of now it seems that g++ cannot compile simple C++ program without creating temporary files, despite use of "-pipe" option:

Code: Select all

pi@raspberrypi400:~/memrun/C/tmp $ g++ --help | grep "\-pipe"
  -pipe                    Use pipes rather than intermediate files.
pi@raspberrypi400:~/memrun/C/tmp $ 

P.S:
Default without using TMPDIR is storage of temporary files in "/tmp", which is not a tmpfs but a normal filesystem, like the seen alternatives "/var/tmp", "/usr/tmp" and current directory. So setting TMPDIR to a tmpfs directory as @Trejan showed seems to be a good idea for compiling bigger projects.
https://github.com/Hermann-SW/memrun
https://stamm-wilbrandt.de/2wheel_balancing_robot
https://stamm-wilbrandt.de/en#raspcatbot
https://github.com/Hermann-SW/Raspberry_v1_camera_global_external_shutter
https://stamm-wilbrandt.de/en/Raspberry_camera.html

User avatar
HermannSW
Posts: 4667
Joined: Fri Jul 22, 2016 9:09 pm
Location: Eberbach, Germany
Contact: Website Twitter YouTube

Re: memfd_create system call for bash

Fri Sep 24, 2021 7:19 am

@Trejan
For using /dev/shm for the executable as you proposed, see @dickon*s security complaints -- that was the reason I did not store executable in /tmp (or /dev/shm):
viewtopic.php?f=66&t=319603&p=1915309#p1913693

So using memfd_create is at least more secure than your /dev/shm storage for executable.


And using memfd_create for just a file (in C or bash) has the advantage, that only current process can access it, and that memfd_create system call was created for a reason. From man page:
memfd_create.png
memfd_create.png
memfd_create.png (36.87 KiB) Viewed 1597 times
https://github.com/Hermann-SW/memrun
https://stamm-wilbrandt.de/2wheel_balancing_robot
https://stamm-wilbrandt.de/en#raspcatbot
https://github.com/Hermann-SW/Raspberry_v1_camera_global_external_shutter
https://stamm-wilbrandt.de/en/Raspberry_camera.html

trejan
Posts: 3736
Joined: Tue Jul 02, 2019 2:28 pm

Re: memfd_create system call for bash

Fri Sep 24, 2021 8:02 am

HermannSW wrote:
Fri Sep 24, 2021 7:19 am
@Trejan
For using /dev/shm for the executable as you proposed, see @dickon*s security complaints -- that was the reason I did not store executable in /tmp (or /dev/shm):
viewtopic.php?f=66&t=319603&p=1915309#p1913693

So using memfd_create is at least more secure than your /dev/shm storage for executable.
mktemp fixes most of those issues. It will give you a unique directory so it can’t clash with something else.

What remains is the problem about why you’re using C as a scripting language…

User avatar
jahboater
Posts: 7400
Joined: Wed Feb 04, 2015 6:38 pm
Location: Wonderful West Dorset

Re: memfd_create system call for bash

Fri Sep 24, 2021 8:14 am

trejan wrote:
Fri Sep 24, 2021 8:02 am
What remains is the problem about why you’re using C as a scripting language…
It is educational.
I have been using Linux and UNIX for 40 years or so, yet I had never come across memfd_create() !!!!!!

A common solution was for the process to create a file in /tmp and then unlink it.
The file remains usable by the process, but is invisible in the file system. When closed it goes completely.
memfd_create() seems more elegant!

Its also interesting that -pipe still uses some files in /tmp

Good thing /tmp is mounted as tmpfs on all my computers (including my 256MB Pi1).

trejan
Posts: 3736
Joined: Tue Jul 02, 2019 2:28 pm

Re: memfd_create system call for bash

Fri Sep 24, 2021 8:20 am

jahboater wrote:
Fri Sep 24, 2021 8:14 am
memfd_create() seems more elegant!
I don’t agree. Having to find the PID and then kill it is messy.

User avatar
jahboater
Posts: 7400
Joined: Wed Feb 04, 2015 6:38 pm
Location: Wonderful West Dorset

Re: memfd_create system call for bash

Fri Sep 24, 2021 8:43 am

trejan wrote:
Fri Sep 24, 2021 8:20 am
jahboater wrote:
Fri Sep 24, 2021 8:14 am
memfd_create() seems more elegant!
I don’t agree. Having to find the PID and then kill it is messy.
Yes true, when its being used by another process (as here).
If its just a fast anonymous temporary file for one process (like the old unlink trick) then closing it is enough.
Last edited by jahboater on Fri Sep 24, 2021 8:53 am, edited 1 time in total.

GlowInTheDark
Posts: 2041
Joined: Sat Nov 09, 2019 12:14 pm

Re: memfd_create system call for bash

Fri Sep 24, 2021 8:52 am

To OP:

If you really wanted to make this slick, you'd do it up as a bash built-in (loadable command). That would eliminate a lot of the issues involved with parsing the output, etc. I would have it report the fd of the opened file, then you'd just close that in bash, using the usual syntax for closing an fd.

Once you get the hang of it, it's not that hard to write.
Poster of inconvenient truths.

Back from a short, unplanned vacation. Did you miss me?

User avatar
HermannSW
Posts: 4667
Joined: Fri Jul 22, 2016 9:09 pm
Location: Eberbach, Germany
Contact: Website Twitter YouTube

Re: memfd_create system call for bash

Fri Sep 24, 2021 9:12 am

GlowInTheDark wrote:
Fri Sep 24, 2021 8:52 am
I would have it report the fd of the opened file, then you'd just close that in bash, using the usual syntax for closing an fd.
This is an example proc file name memfd_create.c returns:

Code: Select all

/proc/1808/fd/3

It is of the form /proc/"pid"/fd/"file descriptor".
Only returning the file descriptor to bash does not allow bash to access the file since the pid is unknown.
I tried to use "$!" in bash script, but that always contains the pid of the bash script, and not of executed "create_memfd".
Extracting the pid to kill running "memfd_create" process does not look that difficult to me:

Code: Select all

pi@raspberrypi400:~/memrun/C $ sed -n "/terminate/,/kill/p"  memfd_create4bash
# terminate memfd_create.c by its pid, in order to close memory file
IFS="/"  read -ra mfa <<< "$mf"
kill "${mfa[2]}"
pi@raspberrypi400:~/memrun/C $ 

In case you have a more elegant way to return both to bash script (proc name and pid), please let me know:
https://github.com/Hermann-SW/memrun/bl ... d_create.c
https://github.com/Hermann-SW/memrun/bl ... reate4bash

Accessing the file by file descriptor in bash script does not work, because the file descriptor is valied in C program memfd_create, not in bash shell. In bash "/proc/PID/fd/FD" needs to be used to access the file with file descriptor FD and memfd_create process pid PID.

jahboater wrote:
Fri Sep 24, 2021 8:14 am
memfd_create() seems more elegant!
I agree, and there must have been a reason that memfd_create system call was added to 3.17 Linux kernel.
jahboater wrote:
Fri Sep 24, 2021 8:14 am
I have been using Linux and UNIX for 40 years or so,
For me it is 32 years as of now.
https://github.com/Hermann-SW/memrun
https://stamm-wilbrandt.de/2wheel_balancing_robot
https://stamm-wilbrandt.de/en#raspcatbot
https://github.com/Hermann-SW/Raspberry_v1_camera_global_external_shutter
https://stamm-wilbrandt.de/en/Raspberry_camera.html

GlowInTheDark
Posts: 2041
Joined: Sat Nov 09, 2019 12:14 pm

Re: memfd_create system call for bash

Fri Sep 24, 2021 10:48 am

What I wrote is correct. I could explain it, but I am not sure if you are interested in my suggestion(s) or not.

Please let me know.

Edit to add: I should note that I really like what you're doing here - and don't mean to take anything away or insult your efforts. I was merely suggesting a way to "take it to the next level".
Poster of inconvenient truths.

Back from a short, unplanned vacation. Did you miss me?

trejan
Posts: 3736
Joined: Tue Jul 02, 2019 2:28 pm

Re: memfd_create system call for bash

Fri Sep 24, 2021 4:29 pm

HermannSW wrote:
Fri Sep 24, 2021 9:12 am
I agree, and there must have been a reason that memfd_create system call was added to 3.17 Linux kernel.
There is no profound reason behind it. memfd_create is a shortcut wrapper to allow you to use /dev/shm without actually having /dev/shm mounted. Some embedded devices don't want to have /dev/shm mounted so this was the workaround.

The SHM file sealing aspects aren't related or required. memfd_create ended up being rolled up together with file sealing into the same patchset. They wanted the ability to share a buffer but also restrict the other recipients from being able to modify it. e.g. a framebuffer being passed to the display engine.

User avatar
HermannSW
Posts: 4667
Joined: Fri Jul 22, 2016 9:09 pm
Location: Eberbach, Germany
Contact: Website Twitter YouTube

Re: memfd_create system call for bash

Fri Sep 24, 2021 8:32 pm

GlowInTheDark wrote:
Fri Sep 24, 2021 10:48 am
What I wrote is correct. I could explain it, but I am not sure if you are interested in my suggestion(s) or not.

Please let me know.

Edit to add: I should note that I really like what you're doing here - and don't mean to take anything away or insult your efforts. I was merely suggesting a way to "take it to the next level".
I am definitely interested, in case it did sound differently, I am not a native English speaker.
GlowInTheDark wrote:
Fri Sep 24, 2021 8:52 am
I would have it report the fd of the opened file, then you'd just close that in bash, using the usual syntax for closing an fd.
The fd is a number, and only has a meaning for a process. I passed "/proc/PID/fd/FD" to bash, because bash has no idea what FD means (because bash has a different pid).


After I learned from @Trejan and my own analysis, that gcc/g++ absolutely needs a directory for temp files, I thought on "why not use the memory file, create filesystem, mount and use". Mounting requires a mount point. But mounting can happen on a non-empty directory! Since memfd_create just runs and waits to be killed, I did misuse "/proc/PID/fd" as mount point for the newly created filesystem!

Here is demo output, it is really fast:

Code: Select all

/proc/25916/fd
total 13
-rw-r--r-- 1 pi   pi       4 Sep 24 22:23 bar
drwx------ 2 root root 12288 Sep 24 22:23 lost+found
foo

real	0m0.293s
user	0m0.045s
sys	0m0.175s
pi@raspberrypi400:~/memrun/C $ 

This is the complete demo:
https://github.com/Hermann-SW/memrun/bl ... te_fs_demo

First creation of memory file with proc name $mf, then fill with 10MB 0s and create filesystem:

Code: Select all

#!/usr/bin/bash

# read memory file name
read -r mf < <(./memfd_create &)

# create 10MB ext2 filesystem in memory file
dd if=/dev/zero of=$mf bs=1024 count=10240 2> /dev/null
mkfs.ext2 $mf 2>&1 | cat > /dev/null 

Extracting pid, creating mount point from it, mount (has to be done as root), change ownership back to user pi for use:

Code: Select all

IFS="/"  read -ra mfa <<< "$mf"

mnt=/proc/${mfa[2]}/fd
sudo mount $mf $mnt
sudo chown pi. $mnt

# show name of mounted filesystem
echo $mnt


Demo use of the new filesystem, final kill will remove memory file and automatically umount the filesystem:

Code: Select all

# create new file
echo "foo" > $mnt/bar

# show directory
ls -l $mnt

# show created file
cat $mnt/bar

# terminate memfd_create.c by its pid, in order to close memory file
kill "${mfa[2]}"
Plan is to do "TMPDIR=$mnt" befor executing gcc/g++, then all files end up in memory file filesystem, that is only accessible to process (compared to use of mktemp in /dev/shm).

Thoughts?
https://github.com/Hermann-SW/memrun
https://stamm-wilbrandt.de/2wheel_balancing_robot
https://stamm-wilbrandt.de/en#raspcatbot
https://github.com/Hermann-SW/Raspberry_v1_camera_global_external_shutter
https://stamm-wilbrandt.de/en/Raspberry_camera.html

GlowInTheDark
Posts: 2041
Joined: Sat Nov 09, 2019 12:14 pm

Re: memfd_create system call for bash

Sat Sep 25, 2021 8:52 am

OK, so no worries. This is a great thread if, for no other reason, to inform people (e.g., me) about new(ish) and fairly obscure Linux system calls (here, the memfd_create).

I have two general comments on the implementation of the main program (memfd_create.c). Note that I don't have access to the actual code, since it somehow got posted to the forum as a graphic (png) file and I don't have OCR software available at the moment. (No, I don't need to be pointed at github or whatever that is...)

1) Here's (the general idea) of how I would have written it (if I follow your idea of doing it as a standalone executable; really, I'd do it the way I suggest later - as a bash "builtin"):

Code: Select all

/* After having opened the memfd file */
int pid;

if (!(pid = fork())) pause();
char buff[100];
/* Do the sprintf, using pid, not getpid() */
sprintf...
puts...
return 0;
So the child process sleeps forever (until killed), holding the memfd open, but the parent exits after printing the message.

2) My original suggestion was to do it as bash "builtin". To do this, you take your code and combine it with a bunch of boilerplate stuff from the bash distribution, then compile it as shared library. Then you use the "enable" command in bash to load your shared library into a running instance of bash, and then the new command (essentially) becomes part of bash itself.

I did something similar to what this thread is about, where the builtin command would set a bash variable, in your case, say memfd_fd, to the fd of the opened memfd object. Then, you can use it in bash code like this:

echo "THis is a test" >&$memfd_fd

Furthermore, any programs spawned by bash while the memfd object is still alive, will inherit fd 3 (or whatever, but in practice, it is almost always 3) as already open and usable.

In general, I think you'd like it. Note the key thing here is that (if you do it as a bash builtin) is that there is no pid to worry about. Or, to put it another way, the pid is just $$ (the pid of the shell itself).

Nitpickers notes on comment 2) above:
1) I tested the syntax of setting a variable, then doing: >&$varname
and it works. But there are some situations where you have to put the varname inside braces (e.g., {varname}) in order for it to be syntactically valid. I usually test this on a case-by-case basis, and use what works.

2) According to "man bash", inheritance of open fds (i.e., fds open in the shell at the time of a program launch) isn't guaranteed for fds greater than 2, but in practice, it seems to always work, so I don't worry about it too much...
Poster of inconvenient truths.

Back from a short, unplanned vacation. Did you miss me?

User avatar
jahboater
Posts: 7400
Joined: Wed Feb 04, 2015 6:38 pm
Location: Wonderful West Dorset

Re: memfd_create system call for bash

Sat Sep 25, 2021 10:02 am

HermannSW wrote:
Fri Sep 24, 2021 8:32 pm
After I learned from @Trejan and my own analysis, that gcc/g++ absolutely needs a directory for temp files, I thought on "why not use the memory file, create filesystem, mount and use".
Again, interesting implementation!
But I just add this to /etc/fstab and any temp files GCC uses will be in memory:

Code: Select all

tmpfs /tmp tmpfs defaults,noatime 0 0

GlowInTheDark
Posts: 2041
Joined: Sat Nov 09, 2019 12:14 pm

Re: memfd_create system call for bash

Sat Sep 25, 2021 10:20 am

But I just add this to /etc/fstab and any temp files GCC uses will be in memory:
But off-topic in this thread.

Might be on-topic elsewhere; don't know.

And here's the thing. If the poster were a newb, and it looked like he just wants a solution to his underlying problem, then talking about alternatives might be useful. But that's not the case here. The poster is obviously "one of us" and he's here to talk about and educate us on some new thing he's found and which he thinks (correctly) is cool. I think that is behavior to be encouraged, not discouraged.

The problem with forums is that they tend to discourage any sort of discussion other than:

newb: problem including proposed solution
oldGuys: Alternative solution
Poster of inconvenient truths.

Back from a short, unplanned vacation. Did you miss me?

User avatar
jahboater
Posts: 7400
Joined: Wed Feb 04, 2015 6:38 pm
Location: Wonderful West Dorset

Re: memfd_create system call for bash

Sat Sep 25, 2021 11:09 am

GlowInTheDark wrote:
Sat Sep 25, 2021 10:20 am
And here's the thing. If the poster were a newb, and it looked like he just wants a solution to his underlying problem, then talking about alternatives might be useful. But that's not the case here. The poster is obviously "one of us" and he's here to talk about and educate us on some new thing he's found and which he thinks (correctly) is cool. I think that is behavior to be encouraged, not discouraged.
Yes.
GlowInTheDark wrote:
Sat Sep 25, 2021 10:20 am
The problem with forums is that they tend to discourage any sort of discussion other than:

newb: problem including proposed solution
oldGuys: Alternative solution
In general, yes. But I don't think that in itself is bad.
If the old_guy suggests a simpler, or otherwise better, alternative, I would want to hear it.

GlowInTheDark
Posts: 2041
Joined: Sat Nov 09, 2019 12:14 pm

Re: memfd_create system call for bash

Sat Sep 25, 2021 12:56 pm

I think - and, yes, I know this is controversial - that alternative solutions belong in separate threads.

Especially if the poster is not a newb.
Poster of inconvenient truths.

Back from a short, unplanned vacation. Did you miss me?

User avatar
HermannSW
Posts: 4667
Joined: Fri Jul 22, 2016 9:09 pm
Location: Eberbach, Germany
Contact: Website Twitter YouTube

Re: memfd_create system call for bash

Sun Sep 26, 2021 7:20 pm

jahboater wrote:
Sat Sep 25, 2021 10:02 am
Again, interesting implementation!
But I just add this to /etc/fstab and any temp files GCC uses will be in memory:

Code: Select all

tmpfs /tmp tmpfs defaults,noatime 0 0
Good solution, but I agree with @GlowInTheDark that you should create a new thread on this. I use gcc for more than 30 years now and it was not clear to me that gcc creates temporary files, nor that it does by default in /tmp which is not a tmpfs in Raspberry Pi OS. You should either link to the investigation posting, or mention all facts from this posting:
viewtopic.php?f=31&t=320170&p=1917561#p1917142
Especially using strace (which I learned about from my older son) for tracing all system calls is interesting to know.

I just posted on adding "-run" option for gcc/g++ thread,
viewtopic.php?f=33&t=319919#p1917846
and chose solution to create memory file, create filesystem in that memory file, mount that, use that to store executable as well as temporary files (using TMPDIR variable), execute executable and then just free the memory file. So like a popup RAM disk that gets used and thrown away when not needed anymore. Reason is that I wanted the "-run" enabled gcc/g++ to just work after "git clone" without any additional changes (bin/grun even compiles memfd_create.c if needed). While changing /tmp to be tmpfs in /etc/fstab needs user to change something, and to convince why:

Code: Select all

pi@raspberrypi400:~/memrun/C $ fortune -s | bin/g++ -run demo.cpp foo 123
bar foo
Sorry.  I forget what I was going to say.
pi@raspberrypi400:~/memrun/C $ 
GlowInTheDark wrote:
Sat Sep 25, 2021 8:52 am
OK, so no worries. This is a great thread if, for no other reason, to inform people (e.g., me) about new(ish) and fairly obscure Linux system calls (here, the memfd_create).
It was new to me as well.
2) My original suggestion was to do it as bash "builtin". To do this, you take your code and combine it with a bunch of boilerplate stuff from the bash distribution, then compile it as shared library. Then you use the "enable" command in bash to load your shared library into a running instance of bash, and then the new command (essentially) becomes part of bash itself.
That sounds interesting, and I have untared "bash-3.1.tar" already and looked into. This is new to me, and needs more time.

1) Here's (the general idea) of how I would have written it (if I follow your idea of doing it as a standalone executable; really, I'd do it the way I suggest later - as a bash "builtin"):

Code: Select all

/* After having opened the memfd file */
int pid;

if (!(pid = fork())) pause();
char buff[100];
/* Do the sprintf, using pid, not getpid() */
sprintf...
puts...
return 0;
So the child process sleeps forever (until killed), holding the memfd open, but the parent exits after printing the message.
Thanks, I used that idea with latest commit.

The new memfd_create.c is used now in demo bash scripts memfd_create4bash as well as in memfd_create_fs_demo, as well as in bin/grun which is the file bin/gcc as well as bin/g++ link to. Instead of returning proc name of memory file, now pid as well as mfd (memory file id) get returned. That way bash script has not to extract pid as substring for final kill of memfd_create process, and concatenation where needed is easy, as done in bin/grun before creating and mounting filesystem:
https://github.com/Hermann-SW/memrun/bl ... n/grun#L38

Code: Select all

...
# read memory file name
read -r pid mfd < <($mfc &)
mnt="/proc/$pid/fd"
mf="$mnt/$mfd"
...

The "pause()" call would be sufficient at end of memfd_create.c, as replacement for my forever sleep() loop. But I agree with you that forked child that just protects memory file is much more close in code to memory file creation (clang-tidy forced me to put brackets around "pause()") :
https://github.com/Hermann-SW/memrun/bl ... d_create.c

Code: Select all

// create memory file by memfd_create system call, for use in bash scripts
//
#include <stdio.h>
#define __USE_GNU
#include <sys/mman.h>
#include <unistd.h>

int main(void)
{
  // create memory file
  int mfd = memfd_create("rab.oof", MFD_CLOEXEC);

Code: Select all

  // child waits for signal, keeps memory file alive
  int pid;
  if (!(pid = fork()))  { pause(); }

  // return pid and mfd
  printf("%d %d\n", pid, mfd);
  fflush(stdout);

  return 0;
}
https://github.com/Hermann-SW/memrun
https://stamm-wilbrandt.de/2wheel_balancing_robot
https://stamm-wilbrandt.de/en#raspcatbot
https://github.com/Hermann-SW/Raspberry_v1_camera_global_external_shutter
https://stamm-wilbrandt.de/en/Raspberry_camera.html

User avatar
jahboater
Posts: 7400
Joined: Wed Feb 04, 2015 6:38 pm
Location: Wonderful West Dorset

Re: memfd_create system call for bash

Sun Sep 26, 2021 8:19 pm

HermannSW wrote:
Sun Sep 26, 2021 7:20 pm
Especially using strace (which I learned about from my older son) for tracing all system calls is interesting to know.
I find the -c option for strace gives a useful summary.
Here are the outputs with, and without, -pipe for compiling a tiny program.

Code: Select all

$ strace -c gcc -pipe hello.c -o /dev/null
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 94.43    0.063400       21133         3           wait4
  1.27    0.000854          14        58        42 faccessat
  0.90    0.000601          66         9           mmap
  0.85    0.000572           8        64        31 newfstatat
  0.54    0.000361          60         6           mprotect
  0.45    0.000304          33         9         2 openat
  0.44    0.000295          19        15           close
  0.27    0.000184          46         4           brk
  0.26    0.000176          35         5           fstat
  0.19    0.000128          18         7           read
  0.18    0.000119         119         1           munmap
  0.08    0.000054          54         1           execve
  0.08    0.000053          17         3           clone
  0.04    0.000024          12         2           unlinkat
  0.02    0.000011           2         4           pipe2
  0.01    0.000004           2         2           getpid
  0.00    0.000000           0         4           ioctl
  0.00    0.000000           0         9           rt_sigaction
  0.00    0.000000           0         2           prlimit64
------ ----------- ----------- --------- --------- ----------------
100.00    0.067140                   208        75 total

Code: Select all

$ strace -c gcc hello.c -o /dev/null
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
100.00    0.032936       10978         3           wait4
  0.00    0.000000           0         4           ioctl
  0.00    0.000000           0         3           unlinkat
  0.00    0.000000           0        58        42 faccessat
  0.00    0.000000           0        10         2 openat
  0.00    0.000000           0        14           close
  0.00    0.000000           0         3           pipe2
  0.00    0.000000           0         7           read
  0.00    0.000000           0        65        31 newfstatat
  0.00    0.000000           0         5           fstat
  0.00    0.000000           0         9           rt_sigaction
  0.00    0.000000           0         3           getpid
  0.00    0.000000           0         4           brk
  0.00    0.000000           0         1           munmap
  0.00    0.000000           0         3           clone
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         9           mmap
  0.00    0.000000           0         6           mprotect
  0.00    0.000000           0         2           prlimit64
------ ----------- ----------- --------- --------- ----------------
100.00    0.032936                   210        75 total

GlowInTheDark
Posts: 2041
Joined: Sat Nov 09, 2019 12:14 pm

Re: memfd_create system call for bash

Mon Sep 27, 2021 11:21 am

# read memory file name
read -r pid mfd < <($mfc &)
Please remove the & from the above command. The whole point of doing the fork() and letting the child process stay alive while the parent exits is so that you don't need to mess around with backgrounding the parent process.
Poster of inconvenient truths.

Back from a short, unplanned vacation. Did you miss me?

User avatar
HermannSW
Posts: 4667
Joined: Fri Jul 22, 2016 9:09 pm
Location: Eberbach, Germany
Contact: Website Twitter YouTube

Re: memfd_create system call for bash

Wed Sep 29, 2021 8:24 am

GlowInTheDark wrote:
Mon Sep 27, 2021 11:21 am
# read memory file name
read -r pid mfd < <($mfc &)
Please remove the & from the above command. The whole point of doing the fork() and letting the child process stay alive while the parent exits is so that you don't need to mess around with backgrounding the parent process.
Thanks, done with this commit:
https://github.com/Hermann-SW/memrun/co ... aca52eb230

Works:

Code: Select all

pi@raspberrypi400:~/memrun/C $ rm memfd_create
pi@raspberrypi400:~/memrun/C $ ls -l memfd_create
ls: cannot access 'memfd_create': No such file or directory
pi@raspberrypi400:~/memrun/C $ fortune -s | bin/g++ -run demo.cpp 123 foo
bar 123
Put your brain in gear before starting your mouth in motion.
pi@raspberrypi400:~/memrun/C $ ls -l memfd_create
-rwxr-xr-x 1 pi pi 8180 Sep 29 10:20 memfd_create
pi@raspberrypi400:~/memrun/C $
https://github.com/Hermann-SW/memrun
https://stamm-wilbrandt.de/2wheel_balancing_robot
https://stamm-wilbrandt.de/en#raspcatbot
https://github.com/Hermann-SW/Raspberry_v1_camera_global_external_shutter
https://stamm-wilbrandt.de/en/Raspberry_camera.html

GlowInTheDark
Posts: 2041
Joined: Sat Nov 09, 2019 12:14 pm

Re: memfd_create system call for bash

Mon Oct 04, 2021 8:58 pm

Just for fun, I threw together a bash builtin version of this. Below are 3 files. They work as is on the system they were developed on (an x86 Ubuntu machine). You may have to mess around a bit to get it to work on the Pi. All of these files were written by me, pretty much from scratch, based, of course, on stuff in the bash distribution.

memfd_create.c:

Code: Select all

/*
    A version of memfd_create(2) for bash
    Note: This looks a lot more complicated than it is.  It actually didn't take long at all to write; I just took a previous one and did s/oldthing/memfd_create/g and I was very close to done.
*/

#include <config.h>

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <linux/memfd.h>

#include "loadables.h"

extern int memfd_create(char *,unsigned);

int
memfd_create_builtin (list)
     WORD_LIST *list;
{
    if (list) {		/* Must have zero args! */
	builtin_usage();
	return EX_USAGE;
	}

    char buff[512];
    int mfd = memfd_create("rab.oof",MFD_CLOEXEC);

    if (mfd == -1) {
	perror("memfd_create");
	return EXECUTION_FAILURE;
	}
    sprintf(buff,"%d",mfd);
    bind_variable("memfd_create",buff,0);
    return EXECUTION_SUCCESS;
}

int
memfd_create_builtin_load (s)
     char *s;
{
    fprintf (stderr,"%s builtin loaded\n",s);
    return (1);
}

void
memfd_create_builtin_unload (s)
     char *s;
{
    fprintf (stderr,"%s builtin unloaded\n",s);
}

/* An array of strings forming the `long' documentation for a builtin xxx,
   which is printed by `help xxx'.  It must end with a NULL.  By convention,
   the first line is a short description. */
char *memfd_create_doc[] = {
	"",
	"A bash builtin to allow a bash script to open a memfd object.",
	"",
	"Usage: memfd_create",
	"",
	"This command takes no args!",
	"",
	"If successful, shell variable 'memfd_create' is set to the fd of the memfd object",
	"",
	"Compiled: " __DATE__ " at " __TIME__,
	"",
	(char *) NULL
};

/* The standard structure describing a builtin command.  bash keeps an array
   of these structures.  The flags must include BUILTIN_ENABLED so the
   builtin can be used. */
struct builtin memfd_create_struct = {
	"memfd_create",		/* builtin name */
	memfd_create_builtin,		/* function implementing the builtin */
	BUILTIN_ENABLED,	/* initial flags for builtin */
	memfd_create_doc,		/* array of long documentation strings. */
	"memfd_create",		/* usage synopsis; becomes short_doc */
	0			/* reserved for internal use */
};
loadables.h:

Code: Select all

/* loadables.h -- Include files needed by all loadable builtins */

/* Copyright (C) 2015 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   Bash is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with Bash.  If not, see <http://www.gnu.org/licenses/>.
*/


#ifndef __LOADABLES_H_
#define __LOADABLES_H_

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "builtins.h"
#include "shell.h"
#include "bashgetopt.h"
#include "chartypes.h"
#include "common.h"

#endif
Compile (the script to compile it):
(Note, incidentally, that fPIC is necessary on X86, but not on ARM. But it doesn't hurt anything to include it anyway)

Code: Select all

#!/bin/bash
gcc -fPIC -shared -s -o memfd_create -W -Wall -Werror -DHAVE_CONFIG_H -DSHELL -Ibash_includes \
    -Ibash_includes/include -Ibash_includes/builtins memfd_create.c
Note that the output is a shared library, not an executable, but it does not have the usual .so filename extension. This is the convention when writing bash builtins, since it makes it easier to load them (see below).

To use it, do:

$ enable -f ./memfd_create memfd_create
$ help memfd_create
(see the help)
$ memfd_create
$ ls -lsa /proc/$$/fd (verify that it is there)
$ echo $memfd_create
(will usually be 3)
$ ls -lsa > /proc/$$/fd/$memfd_create
$ cat /proc/$$/fd/$memfd_create
$ exec {memfd-create}>&-

The last command close it. Then you can do:

$ ls -lsa /proc/$$/fd (verify that it is gone)
Last edited by GlowInTheDark on Tue Oct 05, 2021 8:32 am, edited 2 times in total.
Poster of inconvenient truths.

Back from a short, unplanned vacation. Did you miss me?

GlowInTheDark
Posts: 2041
Joined: Sat Nov 09, 2019 12:14 pm

Re: memfd_create system call for bash

Mon Oct 04, 2021 8:59 pm

Note: If you do decide to try to get this working, the hard part is getting all the bash include files together and accessible. You have to root around in the bash distribution to get them.
Poster of inconvenient truths.

Back from a short, unplanned vacation. Did you miss me?

Return to “General programming discussion”