{
	"id": "1fc28af1-5383-4a48-b458-4b56b9a8663e",
	"created_at": "2026-04-06T00:18:01.352568Z",
	"updated_at": "2026-04-10T03:21:26.420485Z",
	"deleted_at": null,
	"sha1_hash": "2012b7fa9f7f5ec48093c1a16e024be2e52300f9",
	"title": "jet",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 77961,
	"plain_text": "jet\r\nArchived: 2026-04-05 17:03:11 UTC\r\n██████╗ ██████╗ ██╗ ██╗ █████╗\r\n╚════██╗██╔═══██╗██║ ██║██╔══██╗\r\n █████╔╝██║ ██║███████║███████║\r\n ╚═══██╗██║ ██║██╔══██║██╔══██║\r\n██████╔╝╚██████╔╝██║ ██║██║ ██║\r\n╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝\r\nWelcome to 3OHA, a place for random notes, thoughts, and factoids that I want to share or remember\r\n3OHA\r\n11 April 2022\r\nUNIX daemonization and the double fork\r\nSome UNIX implants are expected to effectively run as daemons. This is how Stevens teaches us to daemonize a\r\nprocess:\r\nvoid\r\ndaemonize (const char *cmd)\r\n{\r\n /*\r\n * Some missing stuff here\r\n */\r\n [...]\r\n if ((pid = fork()) \u003c 0)\r\n err(EXIT_FAILURE, \"fork fail\");\r\n else if (pid != 0) /* parent */\r\n exit(0)\r\n /* child A */\r\n setsid()\r\n if ((pid = fork()) \u003c 0)\r\n err(EXIT_FAILURE, \"fork fail\");\r\nhttps://0xjet.github.io/3OHA/2022/04/11/post.html\r\nPage 1 of 4\n\nelse if (pid != 0) /* child A * /\r\n exit(0)\r\n /* child B (grandchild) */\r\n do_daemon_stuff();\r\n}\r\nYou might wonder\r\n1. why the double fork() , and\r\n2. why the setsid() in the first child.\r\nTo answer both questions, you need to understand what the controlling terminal of a process is, why it is risky for\r\na daemon to have one, and how to get rid of it.\r\nThe controlling terminal\r\nI assume you are familiar with some fundamental concepts of job control in UNIX:\r\nProcess groups. Each process in the system is a member of a process group. Process groups are identified\r\nby the process group ID (PGID). When a process is created with fork() , the child inherits the PGID of\r\nits parent. One key goal of process groups is that all members of a group can be signalled at once.\r\nProcess group leader. Each process group has a group leader. The process group leader is the process\r\nwhose PID is the same as the PGID.\r\nSessions. Each process group is a member of a session. Sessions are identified by a session ID (SID).\r\nSession leader. Each session has a session leader. The session leader is the process whose PID is the same\r\nas its PGID and its SID. Note that only process leaders can be session leaders.\r\nSession and process group changes. Any process except the group leader can create a new process group\r\nand become its leader. Any process except the group leader can join another process group within the same\r\nsession.\r\nAn important attribute of a process is its controlling terminal. Controlling terminals are, in fact, associated with\r\nsessions: each session can have at most one controlling terminal, and a controlling terminal can be associated with\r\nat most one session. When you create a process with fork() , the child process inherits the controlling terminal\r\nfrom its parent. Thus, all the processes in a session inherit the controlling terminal from the session leader.\r\nControlling terminals are important because they can receive signals from, and send signals to, the process group\r\nin the session which is associated with the controlling terminal.\r\nA process can detach from its controlling terminal by creating a new session with the setsid() function. The\r\nremaining processes in the old session continue having the old controlling terminal. A critical situation happens\r\nwhen a process which has no controlling terminal opens a terminal device file, such as /dev/tty or\r\n/dev/console . Section 11.1.3 of the POSIX.1-2008 standard gives the answer:\r\nIf a session leader has no controlling terminal, and opens a terminal device file that is not already\r\nassociated with a session without using the O_NOCTTY option (see open() ), it is implementation-https://0xjet.github.io/3OHA/2022/04/11/post.html\r\nPage 2 of 4\n\ndefined whether the terminal becomes the controlling terminal of the session leader. If a process which\r\nis not a session leader opens a terminal file, or the O_NOCTTY option is used on open() , then that\r\nterminal shall not become the controlling terminal of the calling process.\r\nThe last sentence is the key to the double-fork technique.\r\nDaemons and controlling terminals\r\nDaemons should not have controlling terminals. If a daemon has a controlling terminal, it can receive signals from\r\nit that might cause it to halt or exit unexpectedly. The setsid() call in the code shown at the beginning of this\r\npost makes child A become the leader of a new session that only contains one process (itself). Child A also\r\nbecomes the group leader of a new group and, more importantly, it will have no controlling terminal. If the parent\r\nprocess had one (for example, because the daemon was launched from an interactive shell), the setsid() call\r\nwill break this association.\r\nAt this point you might wonder why to fork() in the first place instead of just calling setsid() and do the\r\ndaemon stuff in the parent process. The reason is that setsid() will fail if the calling process is a process group\r\nleader. By calling fork() , we guarantee that the newly created process inherits the PGID from its parent and,\r\ntherefore, is not a process leader.\r\nThe reason for the second fork() derives from the discussion above about whether the daemon could regain\r\ncontrol of a controlling terminal. After the setsid() call, the newly created process (child A) is now a session\r\nleader and the POSIX standard leaves the door open for child A to reacquire a controlling terminal. The second\r\nfork() ensures that child B (the grandchild), where the daemon code will run, is not a session leader. Thus, any\r\ncall to open() that could be manipulated to point it to a terminal file will not result in the daemon regaining a\r\ncontrolling terminal.\r\nSome programmers believe that the double-fork technique is unreasonably paranoid. The daemon might not have\r\na single call to open() . Even if it does, it really depends on implementation-defined specifics. Yet, if you want to\r\nbe absolutely sure that your daemon cannot be tricked into acquiring a controlling terminal, the double-fork\r\ntechnique will give you some extra guarantees. This discussion applies to systems that follow System V\r\nsemantics, such as AIX, HP-UX, and Solaris. Linux, despite not being a SysV variant, behaves like SysV for this\r\nparticular case.\r\nThe BSD (and Linux) daemon() function\r\nThe daemon() function present in current Linux systems is for programs that want to detach from the controlling\r\nterminal and run as system daemons. The glibc implementation of this function is based on the function with the\r\nsame name that has been present in BSD since 4.4BSD. Neither the Linux nor the BSD variants implement the\r\ndouble fork. In Linux, this means that a process that has been daemonized using daemon() might regain a\r\ncontrolling terminal.\r\nOther basic steps SysV daemons should do\r\nhttps://0xjet.github.io/3OHA/2022/04/11/post.html\r\nPage 3 of 4\n\nTraditional SysV daemons should execute a number of initial steps to prevent unwanted interactions with the\r\nenvironment. Chapter 13 of Stevens provides a more thorough discussion on this topic:\r\n1. Call umask() to set the file mode creation mask to a value that turns off whatever permissions you want\r\nto deny in files created by the daemon.\r\n2. Call chdir() to change the current working directory for the daemon to either the root directory or any\r\nother specific location where it will do its stuff.\r\n3. Close all file descriptors that the parent might have opened and the daemon inherints.\r\nThese steps are not needed for modern Linux services because the new init systems (notably systemd) already take\r\ncare of most of them. Furthermore, some of these steps interfere with system logging and monitoring in systemd,\r\nwhich might be a good or a bad thing depending on what kind of daemon you are writing. I will not discuss\r\nsystemd daemons further here. That is an interesting topic that deserves its own post.\r\nThe daemon(7) Linux man page lists some extra steps, including:\r\n1. Reset all signal handlers to their defaults unless you have a good reason to define your own signal handler\r\nfor some signal(s).\r\n2. Reset the signal mask using sigprocmask()\r\n3. Remove or reset environmental variables that you do not need.\r\n4. Connect stdin, stdout, and stderr to /dev/null .\r\n5. Write the daemon PID to a file to ensure that the daemon runs only once.\r\n6. Drop privileges, if possible.\r\n7. Notify the process starting the daemon that the daemonization is complete.\r\nNote that these actions make sense for a system daemon, so some of them might not be needed (or even\r\nrecommended!) for other use cases.\r\nExample: minb — a minimal bind shell\r\nI wrote a minimal bind shell that illustrates the double-fork technique. It also performs some (not all, for a reason)\r\nof the extra steps that you should do on a simple implant like this. This example was actually the main reason for\r\nwriting this post. When I gave it to some of my students, I realized many of them did not understand how to do\r\nrobust daemonization.\r\n© 2022 Juan Tapiador\r\nSource: https://0xjet.github.io/3OHA/2022/04/11/post.html\r\nhttps://0xjet.github.io/3OHA/2022/04/11/post.html\r\nPage 4 of 4",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"MITRE"
	],
	"references": [
		"https://0xjet.github.io/3OHA/2022/04/11/post.html"
	],
	"report_names": [
		"post.html"
	],
	"threat_actors": [],
	"ts_created_at": 1775434681,
	"ts_updated_at": 1775791286,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/2012b7fa9f7f5ec48093c1a16e024be2e52300f9.pdf",
		"text": "https://archive.orkl.eu/2012b7fa9f7f5ec48093c1a16e024be2e52300f9.txt",
		"img": "https://archive.orkl.eu/2012b7fa9f7f5ec48093c1a16e024be2e52300f9.jpg"
	}
}