[PATCH] vfs: require verified birth date for file creation

Christian Brauner posted 1 patch 6 hours ago
fs/Kconfig                        |  17 ++++++++
fs/namei.c                        |  45 +++++++++++++++++++++
include/linux/sched.h             |   8 ++++
include/uapi/asm-generic/errno.h  |   2 +
include/uapi/linux/prctl.h        |   4 ++
kernel/sys.c                      |  42 ++++++++++++++++++++
6 files changed, 118 insertions(+)
[PATCH] vfs: require verified birth date for file creation
Posted by Christian Brauner 6 hours ago
New regulation mandates that all digital content
creation operations must be performed by verified adults. As file
creation is the most fundamental content creation primitive in any
operating system, the kernel must enforce age verification at the VFS
layer.

This patch introduces CONFIG_VFS_AGE_VERIFICATION which, when enabled,
requires every process to register a valid birth date via
prctl(PR_SET_BIRTHDATE) before being permitted to create files. The
birth date is stored in struct task_struct and inherited across fork().

File creation will fail with the new ETOOYOUNG error code if:
  (a) no birth date has been registered, or
  (b) the registered birth date indicates the user is under 18 years
      of age.

A new errno, ETOOYOUNG (134), has been added. Userspace is expected to
handle this error by displaying a calming message and suggesting the
user ask a parent or guardian to create the file on their behalf.

The birth date is deliberately NOT cleared across execve() to avoid
the obvious loophole of spawning a new shell to bypass verification.
Some may argue this violates the principle of least privilege. Those
people are probably too young to create files anyway.

Note: setting a birth date that makes the caller appear older than 150
years is rejected with EINVAL, as the kernel does not support vampires
or other immortal entities at this time. Patches to add undead process
support are welcome but will require a separate Kconfig option.

Tested-by: Someone's nephew/niece (confirmed they cannot create files)
Signed-off-by: Christian Brauner <brauner@kernel.org>
---
 fs/Kconfig                        |  17 ++++++++
 fs/namei.c                        |  45 +++++++++++++++++++++
 include/linux/sched.h             |   8 ++++
 include/uapi/asm-generic/errno.h  |   2 +
 include/uapi/linux/prctl.h        |   4 ++
 kernel/sys.c                      |  42 ++++++++++++++++++++
 6 files changed, 118 insertions(+)

diff --git a/fs/Kconfig b/fs/Kconfig
index 1c2036..424242 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -42,6 +42,23 @@ source "fs/crypto/Kconfig"
 source "fs/verity/Kconfig"
 source "fs/notify/Kconfig"

+config VFS_AGE_VERIFICATION
+	bool "Require birth date verification for file creation"
+	default y
+	help
+	  When enabled, every process must register a valid birth date via
+	  prctl(PR_SET_BIRTHDATE, day, month, year) before being allowed to
+	  create files. Processes that have not registered a birth date or
+	  whose registered birth date indicates they are under 18 years of
+	  age will receive -ETOOYOUNG on any file creation attempt.
+
+	  If unsure, say Y. Failure to comply may result in stern letters
+	  from lawyers. You don't want that. Trust us. Say Y.
+
 source "fs/quota/Kconfig"

 source "fs/autofs/Kconfig"
diff --git a/include/uapi/asm-generic/errno.h b/include/uapi/asm-generic/errno.h
index 1..2 100644
--- a/include/uapi/asm-generic/errno.h
+++ b/include/uapi/asm-generic/errno.h
@@ -20,4 +20,6 @@

 #define EHWPOISON	133	/* Memory page has hardware error */

+#define ETOOYOUNG	134	/* Process too young to create content */
+
 #endif
diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h
index 3..4 100644
--- a/include/uapi/linux/prctl.h
+++ b/include/uapi/linux/prctl.h
@@ -328,4 +328,8 @@

 #define PR_LOCK_INDIR_BR_LP_STATUS      82

+/* age verification for file creation */
+#define PR_SET_BIRTHDATE		83
+#define PR_GET_BIRTHDATE		84
+
 #endif /* _LINUX_PRCTL_H */
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 5..6 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1215,6 +1215,14 @@ struct task_struct {
 #endif
 	struct seccomp			seccomp;
 	struct syscall_user_dispatch	syscall_dispatch;
+
+#ifdef CONFIG_VFS_AGE_VERIFICATION
+	/* compliance - birth date for age verification */
+	u8				birthdate_day;
+	u8				birthdate_month;
+	u16				birthdate_year;
+	bool				birthdate_verified;
+#endif

 	/* Thread group tracking: */
 	u64				parent_exec_id;
diff --git a/kernel/sys.c b/kernel/sys.c
index 7..8 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -2345,6 +2345,48 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
 		break;
+
+#ifdef CONFIG_VFS_AGE_VERIFICATION
+	case PR_SET_BIRTHDATE:
+	{
+		u8 day = (u8)arg2;
+		u8 month = (u8)arg3;
+		u16 year = (u16)arg4;
+		struct tm now;
+		int age;
+
+		/* Basic date validation */
+		if (month < 1 || month > 12)
+			return -EINVAL;
+		if (day < 1 || day > 31)
+			return -EINVAL;
+		if (year < 1900)
+			return -EINVAL;
+
+		time64_to_tm(ktime_get_real_seconds(), 0, &now);
+
+		/* The kernel does not support vampires or immortal entities */
+		if ((now.tm_year + 1900) - year > 150)
+			return -EINVAL;
+
+		/* No time travelers either */
+		if (year > (now.tm_year + 1900))
+			return -EINVAL;
+
+		me->birthdate_day = day;
+		me->birthdate_month = month;
+		me->birthdate_year = year;
+		me->birthdate_verified = true;
+
+		age = (now.tm_year + 1900) - year;
+		if (now.tm_mon + 1 < month ||
+		    (now.tm_mon + 1 == month && now.tm_mday < day))
+			age--;
+
+		if (age < 18)
+			pr_info_ratelimited("Process %d (comm: %s) registered as minor (age %d). "
+				"File creation will be denied. Please ask a "
+				"parent or guardian for assistance.\n",
+				task_pid_nr(me), me->comm, age);
+		break;
+	}
+	case PR_GET_BIRTHDATE:
+		if (!me->birthdate_verified)
+			return -EINVAL;
+		if (put_user(me->birthdate_day, (u8 __user *)arg2) ||
+		    put_user(me->birthdate_month, (u8 __user *)arg3) ||
+		    put_user(me->birthdate_year, (u16 __user *)arg4))
+			return -EFAULT;
+		break;
+#endif /* CONFIG_VFS_AGE_VERIFICATION */
+
 	default:
 		error = -EINVAL;
 		break;
diff --git a/fs/namei.c b/fs/namei.c
index 9..10 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -4148,6 +4148,45 @@ static int vfs_mknodat(struct mnt_idmap *idmap, struct dentry *dentry,
 		       umode_t mode, dev_t dev);

+#ifdef CONFIG_VFS_AGE_VERIFICATION
+/**
+ * check_age_verification - verify the calling process has registered a valid
+ *                          birth date and is old enough to create files.
+ *
+ * Returns 0 if the caller is verified as an adult (>= 18 years old).
+ * Returns -ETOOYOUNG if the caller is a minor or has not registered a
+ * birth date.
+ *
+ * This function exists because the fundamental UNIX
+ * principle of "everything is a file" was insufficiently regulated.
+ */
+static int check_age_verification(void)
+{
+	struct task_struct *tsk = current;
+	struct tm now;
+	int age;
+
+	if (!tsk->birthdate_verified) {
+		pr_warn_ratelimited(
+			"Process %d (comm: %s) attempted to create a file "
+			"without age verification. Set birth date via "
+			"prctl(PR_SET_BIRTHDATE, day, month, year).\n",
+			task_pid_nr(tsk), tsk->comm);
+		return -ETOOYOUNG;
+	}
+
+	time64_to_tm(ktime_get_real_seconds(), 0, &now);
+
+	age = (now.tm_year + 1900) - tsk->birthdate_year;
+	if (now.tm_mon + 1 < tsk->birthdate_month ||
+	    (now.tm_mon + 1 == tsk->birthdate_month &&
+	     now.tm_mday < tsk->birthdate_day))
+		age--;
+
+	if (age < 18) {
+		pr_warn_ratelimited(
+			"Process %d (comm: %s) is only %d years old. "
+			"Must be 18 or older to create files. "
+			"Ask a parent or guardian for help.\n",
+			task_pid_nr(tsk), tsk->comm, age);
+		return -ETOOYOUNG;
+	}
+
+	return 0;
+}
+#endif /* CONFIG_VFS_AGE_VERIFICATION */
+
 /**
  * vfs_create - create new file
  * @idmap:	idmap of the mount the inode was found from
@@ -4170,6 +4209,12 @@ int vfs_create(struct mnt_idmap *idmap, struct dentry *dentry, umode_t mode,
 	if (error)
 		return error;

+#ifdef CONFIG_VFS_AGE_VERIFICATION
+	error = check_age_verification();
+	if (error)
+		return error;
+#endif
+
 	if (!dir->i_op->create)
 		return -EACCES;	/* shouldn't it be ENOSYS? */

--
2.49.0
Re: [PATCH] vfs: require verified birth date for file creation
Posted by Darrick J. Wong 5 hours ago
On Wed, Apr 01, 2026 at 02:43:21PM +0200, Christian Brauner wrote:
> New regulation mandates that all digital content
> creation operations must be performed by verified adults. As file
> creation is the most fundamental content creation primitive in any
> operating system, the kernel must enforce age verification at the VFS
> layer.
> 
> This patch introduces CONFIG_VFS_AGE_VERIFICATION which, when enabled,
> requires every process to register a valid birth date via
> prctl(PR_SET_BIRTHDATE) before being permitted to create files. The
> birth date is stored in struct task_struct and inherited across fork().

I assume that starting a new session with setsid() won't clear it
either, then.  Do you think it would be useful to introduce a new
"btime" cgroup controller so that these decisions could be propagated to
entire groups of processes?

> File creation will fail with the new ETOOYOUNG error code if:
>   (a) no birth date has been registered, or
>   (b) the registered birth date indicates the user is under 18 years
>       of age.

This is more properly implemented as an LSM since we /are/ implementing
security policy here.  While we're on the topic of LSMs, do you think it
would be useful to have a LSM to filter write() calls to mounted block
devices?  Though that might be easier to do via blk_holder_ops.

> A new errno, ETOOYOUNG (134), has been added. Userspace is expected to
> handle this error by displaying a calming message and suggesting the
> user ask a parent or guardian to create the file on their behalf.
> 
> The birth date is deliberately NOT cleared across execve() to avoid
> the obvious loophole of spawning a new shell to bypass verification.
> Some may argue this violates the principle of least privilege. Those
> people are probably too young to create files anyway.
> 
> Note: setting a birth date that makes the caller appear older than 150
> years is rejected with EINVAL, as the kernel does not support vampires
> or other immortal entities at this time. Patches to add undead process
> support are welcome but will require a separate Kconfig option.

That's pretty ageist, don't you think?  I might not be 200 years old yet
but after the events of yesterday I certainly feel as though I am.

> Tested-by: Someone's nephew/niece (confirmed they cannot create files)
> Signed-off-by: Christian Brauner <brauner@kernel.org>
> ---
>  fs/Kconfig                        |  17 ++++++++
>  fs/namei.c                        |  45 +++++++++++++++++++++
>  include/linux/sched.h             |   8 ++++
>  include/uapi/asm-generic/errno.h  |   2 +
>  include/uapi/linux/prctl.h        |   4 ++
>  kernel/sys.c                      |  42 ++++++++++++++++++++
>  6 files changed, 118 insertions(+)
> 
> diff --git a/fs/Kconfig b/fs/Kconfig
> index 1c2036..424242 100644
> --- a/fs/Kconfig
> +++ b/fs/Kconfig
> @@ -42,6 +42,23 @@ source "fs/crypto/Kconfig"
>  source "fs/verity/Kconfig"
>  source "fs/notify/Kconfig"
> 
> +config VFS_AGE_VERIFICATION
> +	bool "Require birth date verification for file creation"
> +	default y
> +	help
> +	  When enabled, every process must register a valid birth date via
> +	  prctl(PR_SET_BIRTHDATE, day, month, year) before being allowed to
> +	  create files. Processes that have not registered a birth date or
> +	  whose registered birth date indicates they are under 18 years of
> +	  age will receive -ETOOYOUNG on any file creation attempt.
> +
> +	  If unsure, say Y. Failure to comply may result in stern letters
> +	  from lawyers. You don't want that. Trust us. Say Y.
> +
>  source "fs/quota/Kconfig"
> 
>  source "fs/autofs/Kconfig"
> diff --git a/include/uapi/asm-generic/errno.h b/include/uapi/asm-generic/errno.h
> index 1..2 100644
> --- a/include/uapi/asm-generic/errno.h
> +++ b/include/uapi/asm-generic/errno.h
> @@ -20,4 +20,6 @@
> 
>  #define EHWPOISON	133	/* Memory page has hardware error */
> 
> +#define ETOOYOUNG	134	/* Process too young to create content */
> +
>  #endif
> diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h
> index 3..4 100644
> --- a/include/uapi/linux/prctl.h
> +++ b/include/uapi/linux/prctl.h
> @@ -328,4 +328,8 @@
> 
>  #define PR_LOCK_INDIR_BR_LP_STATUS      82
> 
> +/* age verification for file creation */
> +#define PR_SET_BIRTHDATE		83
> +#define PR_GET_BIRTHDATE		84
> +
>  #endif /* _LINUX_PRCTL_H */
> diff --git a/include/linux/sched.h b/include/linux/sched.h
> index 5..6 100644
> --- a/include/linux/sched.h
> +++ b/include/linux/sched.h
> @@ -1215,6 +1215,14 @@ struct task_struct {
>  #endif
>  	struct seccomp			seccomp;
>  	struct syscall_user_dispatch	syscall_dispatch;
> +
> +#ifdef CONFIG_VFS_AGE_VERIFICATION
> +	/* compliance - birth date for age verification */
> +	u8				birthdate_day;
> +	u8				birthdate_month;
> +	u16				birthdate_year;

You could perhaps express this as a s32 birthdate measuring days since
the Unix epoch, which would enlarge the range to handle lanthanites.

> +	bool				birthdate_verified;

Legally required now, so there's no need to waste memory on a bool. :/

> +#endif
> 
>  	/* Thread group tracking: */
>  	u64				parent_exec_id;
> diff --git a/kernel/sys.c b/kernel/sys.c
> index 7..8 100644
> --- a/kernel/sys.c
> +++ b/kernel/sys.c
> @@ -2345,6 +2345,48 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
>  		break;
> +
> +#ifdef CONFIG_VFS_AGE_VERIFICATION
> +	case PR_SET_BIRTHDATE:
> +	{
> +		u8 day = (u8)arg2;
> +		u8 month = (u8)arg3;
> +		u16 year = (u16)arg4;

The uapi really ought to be a pointer to a struct timespec.

> +		struct tm now;
> +		int age;
> +
> +		/* Basic date validation */
> +		if (month < 1 || month > 12)
> +			return -EINVAL;
> +		if (day < 1 || day > 31)
> +			return -EINVAL;
> +		if (year < 1900)
> +			return -EINVAL;

No, don't open-code date handling, use a library that's already been
debugged.  Why do you allow 2/31/1904?

> +
> +		time64_to_tm(ktime_get_real_seconds(), 0, &now);
> +
> +		/* The kernel does not support vampires or immortal entities */
> +		if ((now.tm_year + 1900) - year > 150)
> +			return -EINVAL;
> +
> +		/* No time travelers either */
> +		if (year > (now.tm_year + 1900))

Pretty sure there's some addition overflow possibilities here.
check_add_overflow?

> +			return -EINVAL;
> +
> +		me->birthdate_day = day;
> +		me->birthdate_month = month;
> +		me->birthdate_year = year;
> +		me->birthdate_verified = true;
> +
> +		age = (now.tm_year + 1900) - year;
> +		if (now.tm_mon + 1 < month ||
> +		    (now.tm_mon + 1 == month && now.tm_mday < day))
> +			age--;
> +
> +		if (age < 18)
> +			pr_info_ratelimited("Process %d (comm: %s) registered as minor (age %d). "
> +				"File creation will be denied. Please ask a "
> +				"parent or guardian for assistance.\n",
> +				task_pid_nr(me), me->comm, age);
> +		break;
> +	}
> +	case PR_GET_BIRTHDATE:
> +		if (!me->birthdate_verified)
> +			return -EINVAL;
> +		if (put_user(me->birthdate_day, (u8 __user *)arg2) ||
> +		    put_user(me->birthdate_month, (u8 __user *)arg3) ||
> +		    put_user(me->birthdate_year, (u16 __user *)arg4))
> +			return -EFAULT;
> +		break;
> +#endif /* CONFIG_VFS_AGE_VERIFICATION */
> +
>  	default:
>  		error = -EINVAL;
>  		break;
> diff --git a/fs/namei.c b/fs/namei.c
> index 9..10 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -4148,6 +4148,45 @@ static int vfs_mknodat(struct mnt_idmap *idmap, struct dentry *dentry,
>  		       umode_t mode, dev_t dev);
> 
> +#ifdef CONFIG_VFS_AGE_VERIFICATION
> +/**
> + * check_age_verification - verify the calling process has registered a valid
> + *                          birth date and is old enough to create files.
> + *
> + * Returns 0 if the caller is verified as an adult (>= 18 years old).
> + * Returns -ETOOYOUNG if the caller is a minor or has not registered a
> + * birth date.
> + *
> + * This function exists because the fundamental UNIX
> + * principle of "everything is a file" was insufficiently regulated.
> + */
> +static int check_age_verification(void)
> +{
> +	struct task_struct *tsk = current;
> +	struct tm now;
> +	int age;
> +
> +	if (!tsk->birthdate_verified) {
> +		pr_warn_ratelimited(
> +			"Process %d (comm: %s) attempted to create a file "
> +			"without age verification. Set birth date via "
> +			"prctl(PR_SET_BIRTHDATE, day, month, year).\n",
> +			task_pid_nr(tsk), tsk->comm);
> +		return -ETOOYOUNG;
> +	}
> +
> +	time64_to_tm(ktime_get_real_seconds(), 0, &now);
> +
> +	age = (now.tm_year + 1900) - tsk->birthdate_year;
> +	if (now.tm_mon + 1 < tsk->birthdate_month ||
> +	    (now.tm_mon + 1 == tsk->birthdate_month &&
> +	     now.tm_mday < tsk->birthdate_day))
> +		age--;
> +
> +	if (age < 18) {
> +		pr_warn_ratelimited(
> +			"Process %d (comm: %s) is only %d years old. "
> +			"Must be 18 or older to create files. "
> +			"Ask a parent or guardian for help.\n",
> +			task_pid_nr(tsk), tsk->comm, age);
> +		return -ETOOYOUNG;

Yeah, this really looks like material for an LSM.

--D

> +	}
> +
> +	return 0;
> +}
> +#endif /* CONFIG_VFS_AGE_VERIFICATION */
> +
>  /**
>   * vfs_create - create new file
>   * @idmap:	idmap of the mount the inode was found from
> @@ -4170,6 +4209,12 @@ int vfs_create(struct mnt_idmap *idmap, struct dentry *dentry, umode_t mode,
>  	if (error)
>  		return error;
> 
> +#ifdef CONFIG_VFS_AGE_VERIFICATION
> +	error = check_age_verification();
> +	if (error)
> +		return error;
> +#endif
> +
>  	if (!dir->i_op->create)
>  		return -EACCES;	/* shouldn't it be ENOSYS? */
> 
> --
> 2.49.0
>
Re: [PATCH] vfs: require verified birth date for file creation
Posted by Linus Torvalds 2 hours ago
On Wed, 1 Apr 2026 at 06:51, Darrick J. Wong <djwong@kernel.org> wrote:
>
> I assume that starting a new session with setsid() won't clear it
> either, then.  Do you think it would be useful to introduce a new
> "btime" cgroup controller so that these decisions could be propagated to
> entire groups of processes?

I suspect you didn't check the calendar date today.

It's that time of year again...

                 Linus