diff --git a/configure b/configure
index f14709e..18cdcd4 100755
*** a/configure
--- b/configure
*************** UUID_EXTRA_OBJS
*** 708,713 ****
--- 708,714 ----
  with_uuid
  with_systemd
  with_selinux
+ with_seccomp
  with_openssl
  with_ldap
  with_krb_srvnam
*************** with_bsd_auth
*** 853,858 ****
--- 854,860 ----
  with_ldap
  with_bonjour
  with_openssl
+ with_seccomp
  with_selinux
  with_systemd
  with_readline
*************** Optional Packages:
*** 1557,1562 ****
--- 1559,1565 ----
    --with-ldap             build with LDAP support
    --with-bonjour          build with Bonjour support
    --with-openssl          build with OpenSSL support
+   --with-seccomp          build with seccomp support
    --with-selinux          build with SELinux support
    --with-systemd          build with systemd support
    --without-readline      do not use GNU Readline nor BSD Libedit for editing
*************** $as_echo "$with_openssl" >&6; }
*** 7897,7902 ****
--- 7900,7940 ----
  
  
  #
+ # Seccomp
+ #
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build with seccomp support" >&5
+ $as_echo_n "checking whether to build with seccomp support... " >&6; }
+ 
+ 
+ 
+ # Check whether --with-seccomp was given.
+ if test "${with_seccomp+set}" = set; then :
+   withval=$with_seccomp;
+   case $withval in
+     yes)
+ 
+ $as_echo "#define USE_SECCOMP 1" >>confdefs.h
+ 
+       ;;
+     no)
+       :
+       ;;
+     *)
+       as_fn_error $? "no argument expected for --with-seccomp option" "$LINENO" 5
+       ;;
+   esac
+ 
+ else
+   with_seccomp=no
+ 
+ fi
+ 
+ 
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_seccomp" >&5
+ $as_echo "$with_seccomp" >&6; }
+ 
+ 
+ #
  # SELinux
  #
  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build with SELinux support" >&5
*************** fi
*** 12407,12412 ****
--- 12445,12500 ----
  
  
  
+ if test "$with_seccomp" = yes; then
+   { $as_echo "$as_me:${as_lineno-$LINENO}: checking for seccomp_init in -lseccomp" >&5
+ $as_echo_n "checking for seccomp_init in -lseccomp... " >&6; }
+ if ${ac_cv_lib_seccomp_seccomp_init+:} false; then :
+   $as_echo_n "(cached) " >&6
+ else
+   ac_check_lib_save_LIBS=$LIBS
+ LIBS="-lseccomp  $LIBS"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+ /* end confdefs.h.  */
+ 
+ /* Override any GCC internal prototype to avoid an error.
+    Use char because int might match the return type of a GCC
+    builtin and then its argument prototype would still apply.  */
+ #ifdef __cplusplus
+ extern "C"
+ #endif
+ char seccomp_init ();
+ int
+ main ()
+ {
+ return seccomp_init ();
+   ;
+   return 0;
+ }
+ _ACEOF
+ if ac_fn_c_try_link "$LINENO"; then :
+   ac_cv_lib_seccomp_seccomp_init=yes
+ else
+   ac_cv_lib_seccomp_seccomp_init=no
+ fi
+ rm -f core conftest.err conftest.$ac_objext \
+     conftest$ac_exeext conftest.$ac_ext
+ LIBS=$ac_check_lib_save_LIBS
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_seccomp_seccomp_init" >&5
+ $as_echo "$ac_cv_lib_seccomp_seccomp_init" >&6; }
+ if test "x$ac_cv_lib_seccomp_seccomp_init" = xyes; then :
+   cat >>confdefs.h <<_ACEOF
+ #define HAVE_LIBSECCOMP 1
+ _ACEOF
+ 
+   LIBS="-lseccomp $LIBS"
+ 
+ else
+   as_fn_error $? "library 'libseccomp' is required for seccomp support" "$LINENO" 5
+ fi
+ 
+ fi
+ 
  # for contrib/sepgsql
  if test "$with_selinux" = yes; then
    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for security_compute_create_name in -lselinux" >&5
*************** else
*** 13050,13055 ****
--- 13138,13154 ----
  fi
  
  
+ fi
+ 
+ if test "$with_seccomp" = yes ; then
+   ac_fn_c_check_header_mongrel "$LINENO" "seccomp.h" "ac_cv_header_seccomp_h" "$ac_includes_default"
+ if test "x$ac_cv_header_seccomp_h" = xyes; then :
+ 
+ else
+   as_fn_error $? "header file <seccomp.h> is required for seccomp support" "$LINENO" 5
+ fi
+ 
+ 
  fi
  
  if test "$with_libxslt" = yes ; then
diff --git a/configure.in b/configure.in
index 805cf86..65b382d 100644
*** a/configure.in
--- b/configure.in
*************** AC_MSG_RESULT([$with_openssl])
*** 842,847 ****
--- 842,856 ----
  AC_SUBST(with_openssl)
  
  #
+ # Seccomp
+ #
+ AC_MSG_CHECKING([whether to build with seccomp support])
+ PGAC_ARG_BOOL(with, seccomp, no, [build with seccomp support],
+               [AC_DEFINE([USE_SECCOMP], 1, [Define to 1 to build with seccomp support. (--with-seccomp)])])
+ AC_MSG_RESULT([$with_seccomp])
+ AC_SUBST(with_seccomp)
+ 
+ #
  # SELinux
  #
  AC_MSG_CHECKING([whether to build with SELinux support])
*************** fi
*** 1234,1239 ****
--- 1243,1252 ----
  AC_SUBST(LDAP_LIBS_FE)
  AC_SUBST(LDAP_LIBS_BE)
  
+ if test "$with_seccomp" = yes; then
+   AC_CHECK_LIB(seccomp, seccomp_init, [], [AC_MSG_ERROR([library 'libseccomp' is required for seccomp support])])
+ fi
+ 
  # for contrib/sepgsql
  if test "$with_selinux" = yes; then
    AC_CHECK_LIB(selinux, security_compute_create_name, [],
*************** if test "$with_libxml" = yes ; then
*** 1389,1394 ****
--- 1402,1411 ----
    AC_CHECK_HEADER(libxml/parser.h, [], [AC_MSG_ERROR([header file <libxml/parser.h> is required for XML support])])
  fi
  
+ if test "$with_seccomp" = yes ; then
+   AC_CHECK_HEADER(seccomp.h, [], [AC_MSG_ERROR([header file <seccomp.h> is required for seccomp support])])
+ fi
+ 
  if test "$with_libxslt" = yes ; then
    AC_CHECK_HEADER(libxslt/xslt.h, [], [AC_MSG_ERROR([header file <libxslt/xslt.h> is required for XSLT support])])
  fi
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 89284dc..c346db7 100644
*** a/doc/src/sgml/config.sgml
--- b/doc/src/sgml/config.sgml
*************** include_dir 'conf.d'
*** 1859,1864 ****
--- 1859,2031 ----
         </para>
        </listitem>
       </varlistentry>
+ 
+      <varlistentry id="guc-seccomp" xreflabel="seccomp">
+       <term><varname>seccomp</varname> (<type>bool</type>)
+       <indexterm>
+        <primary><varname>seccomp</varname> configuration parameter</primary>
+       </indexterm>
+       </term>
+       <listitem>
+        <para>
+         <varname>seccomp</varname> turns on or off seccomp syscall enforcement.
+         This parameter can only be set at server start. The default value is
+         <literal>off</literal>.
+        </para>
+       </listitem>
+      </varlistentry>
+ 
+      <varlistentry id="guc-global-syscall-default" xreflabel="global_syscall_default">
+       <term><varname>global_syscall_default</varname> (<type>enum</type>)
+       <indexterm>
+        <primary><varname>global_syscall_default</varname> configuration parameter</primary>
+       </indexterm>
+       </term>
+       <listitem>
+        <para>
+         <varname>global_syscall_default</varname> determines the default action taken by
+         kernel seccomp enforcement. It is applied to the postmaster and inherited by
+         all child processes.
+        </para>
+ 
+        <para>
+         Valid values are as follows in increasing precedence order.
+         The default value is <literal>allow</literal>, which allows all
+         syscalls not in a specific action list without any action including logging.
+         A value of <literal>log</literal> turns on seccomp enforcement
+         in log-only mode. In this mode, disallowed kernel syscalls are logged by auditd
+         to the audit log. When set to <literal>error</literal>, disallowed kernel syscalls
+         will return with a permission denied error. Finally, <literal>kill</literal> will
+         cause the offending process to be killed as though by a
+         <literal>SIGSYS</literal> signal.
+        </para>
+ 
+        <para>
+         This parameter can only be set at server start.
+        </para>
+       </listitem>
+      </varlistentry>
+ 
+      <varlistentry id="guc-session-syscall-default" xreflabel="session_syscall_default">
+       <term><varname>session_syscall_default</varname> (<type>enum</type>)
+       <indexterm>
+        <primary><varname>session_syscall_default</varname> configuration parameter</primary>
+       </indexterm>
+       </term>
+       <listitem>
+        <para>
+         <varname>session_syscall_default</varname> helps determine the default action taken
+         by kernel seccomp enforcement. It is applied to client backend sessions. The
+         effective value is the either this setting or that of the postmaster,
+         <varname>global_syscall_default</varname>, whichever has the higher precedence.
+        </para>
+ 
+        <para>
+         Valid values are the same as those for <varname>global_syscall_default</varname>.
+        </para>
+ 
+        <para>
+         This parameter can be set by the superuser, however new values only take effect
+         at session start. This makes it possible to customize sessions with the
+         <command>ALTER ROLE SET</command>. For example, a specific role might have it
+         set to <literal>error</literal> with a restrictive session allow list
+         (<varname>session_syscall_allow</varname>), while other roles have it set to
+         <literal>allow</literal>, assuming <varname>global_syscall_default</varname>
+         is also set to <literal>allow</literal>.
+        </para>
+       </listitem>
+      </varlistentry>
+ 
+      <varlistentry id="guc-global-syscall-lists" xreflabel="global_syscall_lists">
+       <term><varname>global_syscall_allow</varname> (<type>string</type>)
+       <indexterm>
+        <primary><varname>global_syscall_allow</varname> configuration parameter</primary>
+       </indexterm>
+       </term>
+       <term><varname>global_syscall_log</varname> (<type>string</type>)
+       <indexterm>
+        <primary><varname>global_syscall_log</varname> configuration parameter</primary>
+       </indexterm>
+       </term>
+       <term><varname>global_syscall_error</varname> (<type>string</type>)
+       <indexterm>
+        <primary><varname>global_syscall_error</varname> configuration parameter</primary>
+       </indexterm>
+       </term>
+       <term><varname>global_syscall_kill</varname> (<type>string</type>)
+       <indexterm>
+        <primary><varname>global_syscall_kill</varname> configuration parameter</primary>
+       </indexterm>
+       </term>
+ 
+       <listitem>
+        <para>
+         These four configuration parameters are lists of kernel syscalls to be given
+         allow, log, error, and kill action rules in the global (postmaster) seccomp
+         filter. They are also inherited by all child processes. Any syscall not explicitly
+         enumerated in one of these lists will have an action as determined by the
+         <varname>global_syscall_default</varname> setting. This parameter can only be set
+         at server start.
+        </para>
+       </listitem>
+      </varlistentry>
+ 
+      <varlistentry id="guc-session-syscall-lists" xreflabel="session_syscall_lists">
+       <term><varname>session_syscall_allow</varname> (<type>string</type>)
+       <indexterm>
+        <primary><varname>session_syscall_allow</varname> configuration parameter</primary>
+       </indexterm>
+       </term>
+       <term><varname>session_syscall_log</varname> (<type>string</type>)
+       <indexterm>
+        <primary><varname>session_syscall_log</varname> configuration parameter</primary>
+       </indexterm>
+       </term>
+       <term><varname>session_syscall_error</varname> (<type>string</type>)
+       <indexterm>
+        <primary><varname>session_syscall_error</varname> configuration parameter</primary>
+       </indexterm>
+       </term>
+       <term><varname>session_syscall_kill</varname> (<type>string</type>)
+       <indexterm>
+        <primary><varname>session_syscall_kill</varname> configuration parameter</primary>
+       </indexterm>
+       </term>
+ 
+       <listitem>
+        <para>
+         These four configuration parameters are lists of kernel syscalls to be given
+         allow, log, error, and kill action rules in the client session backend seccomp
+         filter. Any syscall not explicitly enumerated in one of these lists will have
+         a session filter action as determined by the <varname>session_syscall_default</varname>
+         setting. The actual effective action for any given syscall is the highest
+         precedence action, for that syscall, from either the session filter or the
+         global filter. This setting takes effect on session start and may not be
+         changed once a session is established.
+        </para>
+ 
+        <para>
+         The intent of this feature is to allow further restriction of the syscalls
+         available in an interactive user session. It is also possible to customize
+         sessions with the <command>ALTER ROLE SET</command>. For example, a specific
+         role might be allowed to use the necessary syscalls to enable an untrusted
+         procedural-language function to execute arbitrary system commands, while
+         other roles are denied that permission.
+        </para>
+ 
+        <para>
+         These lists may also be set to the single character <literal>'*'</literal>.
+         When set this way, the corresponding action global list is used without
+         modification.
+        </para>
+ 
+        <para>
+         This parameter can be changed without restarting the server, but changes only
+         take effect when a new session is started.
+        </para>
+ 
+       </listitem>
+      </varlistentry>
       </variablelist>
      </sect2>
  
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index a7abf8c..51f5964 100644
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
*************** SELECT collation for ('foo' COLLATE "de_
*** 19678,19683 ****
--- 19678,19767 ----
      </tgroup>
     </table>
  
+    <para>
+     The functions shown in <xref linkend="functions-seccomp"/>
+     print information about active seccomp filters, both at the
+     global (postmaster) level and session (client backend) level.
+     In particular they calculate the the session level based
+     on the kernel's rules for overlaying the session filter
+     on top of the global filter. Essentially, for any given syscall
+     the most restrictive (highest precedence) rule will govern
+     the action taken.
+    </para>
+ 
+    <para>
+     These functions return no rows unless the <option>--with-seccomp</option>
+     was used during <command>configure</command>.
+    </para>
+ 
+    <table id="functions-seccomp">
+     <title>Seccomp Functions</title>
+     <tgroup cols="3">
+      <thead>
+       <row><entry>Name</entry> <entry>Return Type</entry> <entry>Description</entry></row>
+      </thead>
+ 
+      <tbody>
+       <row>
+        <entry>
+         <indexterm><primary>pg_get_seccomp_filter</primary></indexterm>
+         <literal><function>pg_get_seccomp_filter()</function></literal>
+        </entry>
+        <entry><type>record</type></entry>
+        <entry>
+         Returns information about active seccomp filters.
+        </entry>
+       </row>
+ 
+      </tbody>
+     </tgroup>
+    </table>
+ 
+    <para>
+     <function>pg_get_seccomp_filter</function> returns a record, shown in
+     <xref linkend="functions-pg-get-seccomp-filter"/>
+    </para>
+ 
+    <table id="functions-pg-get-seccomp-filter">
+     <title><function>pg_get_seccomp_filter</function> Columns</title>
+     <tgroup cols="3">
+      <thead>
+       <row>
+        <entry>Column Name</entry>
+        <entry>Data Type</entry>
+        <entry>Description</entry>
+       </row>
+      </thead>
+ 
+      <tbody>
+ 
+       <row>
+        <entry><literal>syscall</literal></entry>
+        <entry><type>text</type></entry>
+        <entry>Name of the kernel syscall</entry>
+       </row>
+ 
+       <row>
+        <entry><literal>syscallnum</literal></entry>
+        <entry><type>int</type></entry>
+        <entry>Kernel syscall number, or -1 for a default rule</entry>
+       </row>
+ 
+       <row>
+        <entry><literal>filter_action</literal></entry>
+        <entry><type>text</type></entry>
+        <entry>Source context -> rule action</entry>
+       </row>
+ 
+       <row>
+        <entry><literal>context</literal></entry>
+        <entry><type>text</type></entry>
+        <entry>Context in which rule is applied</entry>
+       </row>
+ 
+      </tbody>
+     </tgroup>
+    </table>
    </sect1>
  
    <sect1 id="functions-admin">
diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml
index 4493862..939e9e3 100644
*** a/doc/src/sgml/installation.sgml
--- b/doc/src/sgml/installation.sgml
*************** su - postgres
*** 254,259 ****
--- 254,266 ----
  
      <listitem>
       <para>
+       You need <productname>seccomp</productname>, if you want to support
+       kernel syscall filtering.
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
        You need <application>Kerberos</application>, <productname>OpenLDAP</productname>,
        and/or <application>PAM</application>, if you want to support authentication
        using those services.
*************** su - postgres
*** 843,848 ****
--- 850,869 ----
           before proceeding.
          </para>
         </listitem>
+       </varlistentry>
+ 
+       <varlistentry>
+        <term><option>--with-seccomp</option></term>
+        <listitem>
+         <para>
+          Build with support for <indexterm><primary>seccomp</primary></indexterm>
+          kernel syscall filtering. This requires <productname>seccomp</productname>
+          packages to be installed. <filename>configure</filename> will check
+          for the required header files and libraries to make sure that
+          your <productname>seccomp</productname> installation is sufficient
+          before proceeding.
+         </para>
+        </listitem>
        </varlistentry>
  
        <varlistentry>
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index dc3f207..bbdc69b 100644
*** a/src/Makefile.global.in
--- b/src/Makefile.global.in
*************** with_perl	= @with_perl@
*** 185,190 ****
--- 185,191 ----
  with_python	= @with_python@
  with_tcl	= @with_tcl@
  with_openssl	= @with_openssl@
+ with_seccomp	= @with_seccomp@
  with_selinux	= @with_selinux@
  with_systemd	= @with_systemd@
  with_gssapi	= @with_gssapi@
diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index 1119e21..bb2e899 100644
*** a/src/backend/commands/variable.c
--- b/src/backend/commands/variable.c
***************
*** 17,22 ****
--- 17,25 ----
  #include "postgres.h"
  
  #include <ctype.h>
+ #ifdef USE_SECCOMP
+ #include <seccomp.h>
+ #endif
  
  #include "access/htup_details.h"
  #include "access/parallel.h"
*************** show_role(void)
*** 901,903 ****
--- 904,988 ----
  	/* Otherwise we can just use the GUC string */
  	return role_string ? role_string : "none";
  }
+ 
+ #ifdef USE_SECCOMP
+ /*
+  * check_syscall_list: GUC check_hook
+  * check various lists of syscalls used for seccomp enforcement
+  */
+ static bool
+ check_syscall_list(char **newval, void **extra, GucSource source)
+ {
+ 	char		   *rawstring = NULL;
+ 	List		   *elemlist = NIL;
+ 	ListCell	   *l;
+ 	bool			result = true;
+ 
+ 	/* Need a modifiable copy of string */
+ 	rawstring = pstrdup(*newval);
+ 
+ 	/* Parse string into list of syscalls */
+ 	if (!SplitIdentifierString(rawstring, ',', &elemlist))
+ 	{
+ 		GUC_check_errdetail("List syntax is invalid.");
+ 		result = false;
+ 		goto out;
+ 	}
+ 
+ 	foreach(l, elemlist)
+ 	{
+ 		char   *cursyscall = (char *) lfirst(l);
+ 		int		syscallnum;
+ 
+ 		/* resolve the syscall name to its number on the current arch */
+ 		syscallnum = seccomp_syscall_resolve_name(cursyscall);
+ 		if (syscallnum < 0)
+ 		{
+ 			/* invalid syscall name */
+ 			GUC_check_errcode(ERRCODE_INVALID_PARAMETER_VALUE);
+ 			GUC_check_errdetail("Seccomp failed to resolve syscall: \"%s\"",
+ 								cursyscall);
+ 			result = false;
+ 			goto out;
+ 		}
+ 	}
+ 
+ out:
+ 	/* safe to release if NIL */
+ 	list_free(elemlist);
+ 
+ 	/* but pfree is not */
+ 	if (rawstring)
+ 		pfree(rawstring);
+ 
+ 	return result;
+ }
+ #endif
+ 
+ bool
+ check_global_syscall_list(char **newval, void **extra, GucSource source)
+ {
+ #ifdef USE_SECCOMP
+ 	return check_syscall_list(newval, extra, source);
+ #else
+ 	return true;
+ #endif
+ }
+ 
+ bool
+ check_session_syscall_list(char **newval, void **extra, GucSource source)
+ {
+ #ifdef USE_SECCOMP
+ 	/*
+ 	 * If the only character of the passed *newval string is '*'
+ 	 * then use the global allow list. Only applies to children
+ 	 * of the postmaster.
+ 	 */
+ 	if (strlen(*newval) == 1 && *newval[0] == '*')
+ 		return true;
+ 	else
+ 		return check_syscall_list(newval, extra, source);
+ #else
+ 	return true;
+ #endif
+ }
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 62dc93d..2216d49 100644
*** a/src/backend/postmaster/postmaster.c
--- b/src/backend/postmaster/postmaster.c
*************** PostmasterMain(int argc, char *argv[])
*** 963,968 ****
--- 963,982 ----
  	 */
  	LocalProcessControlFile(false);
  
+ #ifdef USE_SECCOMP
+ 	/*
+ 	 * If seccomp filtering is requested, load the global filter.
+ 	 * The list of allowed syscalls may be ratched down further
+ 	 * in specific backends based on the actual needs by backend type.
+ 	 */
+ 	if(!load_seccomp_filter("postmaster"))
+ 	{
+ 		ereport(FATAL,
+ 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 				 errmsg("failed to load global seccomp filter")));
+ 	}
+ #endif
+ 
  	/*
  	 * Initialize SSL library, if specified.
  	 */
diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c
index 5d4f26a..a9df9e4 100644
*** a/src/backend/utils/adt/genfile.c
--- b/src/backend/utils/adt/genfile.c
***************
*** 15,20 ****
--- 15,23 ----
   */
  #include "postgres.h"
  
+ #ifdef USE_SECCOMP
+ #include <seccomp.h>
+ #endif
  #include <sys/file.h>
  #include <sys/stat.h>
  #include <unistd.h>
***************
*** 28,39 ****
--- 31,46 ----
  #include "funcapi.h"
  #include "mb/pg_wchar.h"
  #include "miscadmin.h"
+ #include "nodes/bitmapset.h"
  #include "postmaster/syslogger.h"
  #include "storage/fd.h"
  #include "utils/builtins.h"
+ #include "utils/guc.h"
+ #include "utils/hsearch.h"
  #include "utils/memutils.h"
  #include "utils/syscache.h"
  #include "utils/timestamp.h"
+ #include "utils/varlena.h"
  
  typedef struct
  {
*************** pg_ls_archive_statusdir(PG_FUNCTION_ARGS
*** 669,671 ****
--- 676,1113 ----
  {
  	return pg_ls_dir_files(fcinfo, XLOGDIR "/archive_status", true);
  }
+ 
+ #define NUM_SECCOMP_FILTER_ATTS		4
+ #define NUM_SECCOMP_RULES			400
+ 
+ #ifdef USE_SECCOMP
+ typedef struct seccomp_rule
+ {
+ 	int			syscallnum;		/* syscall number */
+ 	char	   *syscall;		/* syscall name string */
+ 	int			rule_action;	/* action level for this rule */
+ 	char	   *source;			/* filter source for this rule */
+ } seccomp_rule;
+ 
+ typedef struct seccompHashEntry
+ {
+         int					syscallnum;
+         seccomp_rule	   *scr_entry;
+ } seccompHashEntry;
+ 
+ extern const struct config_enum_entry seccomp_options[];
+ 
+ static void
+ init_hash_from_bitmap(Bitmapset *A, int raction, char *source,
+ 					  HTAB *seccompHash)
+ {
+ 	bool				found;
+ 	int					syscallnum;
+ 	char			   *cursyscall;
+ 	seccompHashEntry   *hentry;
+ 
+ 	syscallnum = -1;
+ 	while ((syscallnum = bms_next_member(A, syscallnum)) >= 0)
+ 	{
+ 		seccomp_rule   *scr = palloc(sizeof(seccomp_rule));
+ 
+ 		scr->syscallnum = syscallnum;
+ 
+ 		/*
+ 		 * Resolver returns NULL on error. Given how we got here that
+ 		 * should never happen. We must free() the result to avoid leakage.
+ 		 */
+ 		cursyscall =  seccomp_syscall_resolve_num_arch(seccomp_arch_native(),
+ 													   syscallnum);
+ 		if (cursyscall)
+ 		{
+ 			scr->syscall = pstrdup(cursyscall);
+ 			free(cursyscall);
+ 		}
+ 		scr->rule_action = raction;
+ 		scr->source = source;
+ 
+ 		hentry = (seccompHashEntry *) hash_search(seccompHash,
+ 												  (const void *) &syscallnum,
+ 												  HASH_ENTER, &found);
+ 
+ 		/* should not happen */
+ 		if (found)
+ 			elog(ERROR, "duplicate syscall entry found: source \"%s\"",
+ 						 source);
+ 
+ 		hentry->syscallnum = syscallnum;
+ 		hentry->scr_entry = scr;
+ 	}
+ }
+ 
+ static void
+ ovly_hash_from_bitmap(Bitmapset *A, int raction, char *gsource,
+ 					  int sdef, char *ssource, HTAB *seccompHash)
+ {
+ 	bool				found;
+ 	int					syscallnum;
+ 	char			   *cursyscall;
+ 	seccompHashEntry   *hentry;
+ 
+ 	syscallnum = -1;
+ 	while ((syscallnum = bms_next_member(A, syscallnum)) >= 0)
+ 	{
+ 		seccomp_rule   *scr;
+ 
+ 		hentry = (seccompHashEntry *) hash_search(seccompHash,
+ 												  (const void *) &syscallnum,
+ 												  HASH_ENTER, &found);
+ 
+ 		/*
+ 		 * If an entry does not exist, we can just add it. However,
+ 		 * the default action from the session still wins if it takes
+ 		 * precedence over that of the global rule.
+ 		 *
+ 		 * If an entry does exist, we must determine whether the new
+ 		 * rule precedence overrides the old one.
+ 		 */
+ 		if (!found)
+ 		{
+ 			scr = palloc(sizeof(seccomp_rule));
+ 			scr->syscallnum = syscallnum;
+ 
+ 			/*
+ 			 * Resolver returns NULL on error. Given how we got here that
+ 			 * should never happen. We must free() the result to avoid leakage.
+ 			 */
+ 			cursyscall =  seccomp_syscall_resolve_num_arch(seccomp_arch_native(),
+ 														   syscallnum);
+ 			if (cursyscall)
+ 			{
+ 				scr->syscall = pstrdup(cursyscall);
+ 				free(cursyscall);
+ 			}
+ 			if (raction > sdef)
+ 			{
+ 				scr->rule_action = raction;
+ 				scr->source = gsource;
+ 			}
+ 			else
+ 			{
+ 				scr->rule_action = sdef;
+ 				scr->source = ssource;
+ 			}
+ 
+ 			hentry->syscallnum = syscallnum;
+ 			hentry->scr_entry = scr;
+ 		}
+ 		else
+ 		{
+ 			/* determine if adjustment is necessary */
+ 			scr = hentry->scr_entry;
+ 			if (raction > scr->rule_action)
+ 			{
+ 				/* new rule takes precedence */
+ 				scr->rule_action = raction;
+ 				scr->source = gsource;
+ 			}
+ 		}
+ 	}
+ }
+ 
+ static void
+ ovly_hash_from_default(Bitmapset *A, int raction, char *source,
+ 					  HTAB *seccompHash)
+ {
+ 	bool				found;
+ 	int					syscallnum;
+ 	seccompHashEntry   *hentry;
+ 
+ 	syscallnum = -1;
+ 	while ((syscallnum = bms_next_member(A, syscallnum)) >= 0)
+ 	{
+ 		seccomp_rule   *scr;
+ 
+ 		hentry = (seccompHashEntry *) hash_search(seccompHash,
+ 												  (const void *) &syscallnum,
+ 												  HASH_ENTER, &found);
+ 
+ 		/*
+ 		 * If an entry does not already exist at this point, something
+ 		 * odd is amiss. Should not happen.
+ 		 */
+ 		if (!found)
+ 			elog(ERROR, "failed to find expected session filter syscall " \
+ 						"entry: syscall number %d", syscallnum);
+ 		else
+ 		{
+ 			/* determine if adjustment is necessary */
+ 			scr = hentry->scr_entry;
+ 			if (raction > scr->rule_action)
+ 			{
+ 				/* new rule takes precedence */
+ 				scr->rule_action = raction;
+ 				scr->source = source;
+ 			}
+ 		}
+ 	}
+ }
+ 
+ static const char *
+ get_seccomp_opt_str(int val)
+ {
+ 	const struct config_enum_entry *entry;
+ 
+ 	/* stringify the enforcement action levels */
+ 	for (entry = seccomp_options; entry->name; entry++)
+ 		if (entry->val == val)
+ 			return entry->name;
+ 
+ 	return "unknown";
+ }
+ 
+ static void
+ put_global_rules(Bitmapset *A, int raction, char *source,
+ 				 TupleDesc tupdesc, Tuplestorestate *tupstore)
+ {
+ 	int					syscallnum;
+ 
+ 	syscallnum = -1;
+ 	while ((syscallnum = bms_next_member(A, syscallnum)) >= 0)
+ 	{
+ 		Datum				values[NUM_SECCOMP_FILTER_ATTS];
+ 		bool				nulls[NUM_SECCOMP_FILTER_ATTS];
+ 		char			   *cursyscall;
+ 
+ 		memset(values, 0, sizeof(values));
+ 		memset(nulls, 0, sizeof(nulls));
+ 
+ 		/*
+ 		 * Resolver returns NULL on error. Given how we got here that
+ 		 * should never happen. We must free() the result to avoid leakage.
+ 		 */
+ 		cursyscall =  seccomp_syscall_resolve_num_arch(seccomp_arch_native(),
+ 													   syscallnum);
+ 		if (cursyscall)
+ 		{
+ 			char	   *buf;
+ 
+ 			values[0] = PointerGetDatum(cstring_to_text(cursyscall));
+ 			free(cursyscall);
+ 
+ 			values[1] = Int32GetDatum(syscallnum);
+ 
+ 			buf = psprintf("%s->%s", source, get_seccomp_opt_str(raction));
+ 			values[2] = PointerGetDatum(cstring_to_text(buf));
+ 
+ 			values[3] = PointerGetDatum(cstring_to_text("global"));
+ 
+ 			/* shove row into tuplestore */
+ 			tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ 		}
+ 	}
+ }
+ #endif /* USE_SECCOMP */
+ 
+ Datum
+ pg_get_seccomp_filter(PG_FUNCTION_ARGS)
+ {
+ #ifdef USE_SECCOMP
+ 	seccomp_filter	   *g = global_filter;
+ 	seccomp_filter	   *s = session_filter;
+ 	HASHCTL         	ctl;
+ 	HTAB			   *seccompHash = NULL;
+ 	seccompHashEntry   *hentry;
+ 	seccomp_rule	   *scr = palloc(sizeof(seccomp_rule));
+ 	HASH_SEQ_STATUS		status;
+ 	int					syscallnum;
+ 	char			   *gsource = "global";
+ 	char			   *ssource = "session";
+  	int					gdef = g->def;
+  	int					sdef = s->def;
+  	int					mdef = (gdef > sdef) ? gdef : sdef;
+  	char			   *msource = (gdef > sdef) ? gsource : ssource;
+ 	Bitmapset		   *gunion = NULL;
+ 	Bitmapset		   *sunion = NULL;
+ 	char			   *buf;
+ 	Datum				values[NUM_SECCOMP_FILTER_ATTS];
+ 	bool				nulls[NUM_SECCOMP_FILTER_ATTS];
+ #endif
+ 	ReturnSetInfo	   *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+ 	TupleDesc			tupdesc;
+ 	Tuplestorestate	   *tupstore;
+ 	MemoryContext		per_query_ctx;
+ 	MemoryContext		oldcontext;
+ 
+ 	/* Check to see if caller supports us returning a tuplestore */
+ 	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				 errmsg("set-valued function called in context that cannot accept a set")));
+ 	if (!(rsinfo->allowedModes & SFRM_Materialize))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				 errmsg("materialize mode required, but it is not " \
+ 						"allowed in this context")));
+ 
+ 	/* Switch into long-lived context to construct returned data structures */
+ 	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+ 	oldcontext = MemoryContextSwitchTo(per_query_ctx);
+ 
+ 	/* Build a tuple descriptor for our result type */
+ 	/* need a tuple descriptor representing three TEXT columns */
+ 	tupdesc = CreateTemplateTupleDesc(NUM_SECCOMP_FILTER_ATTS);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "syscall",
+ 					   TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "syscallnum",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "filter_action",
+ 					   TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "context",
+ 					   TEXTOID, -1, 0);
+ 
+ 	/* Build a tuplestore to return our results in */
+ 	tupstore = tuplestore_begin_heap(true, false, work_mem);
+ 	rsinfo->returnMode = SFRM_Materialize;
+ 	rsinfo->setResult = tupstore;
+ 	rsinfo->setDesc = tupdesc;
+ 
+ #ifdef USE_SECCOMP
+ 
+ 	/*
+ 	 * We need to iterate through 4 bitmap sets each, across two filters
+ 	 * (global and session), applying the below logic, in order to
+ 	 * determine which action applies to what syscall. The most
+ 	 * straighforward way to do that seems to be to build a hash
+ 	 * table since the two filter sets may overlap, and the syscall
+ 	 * numbers may vary with architecture.
+ 	 *
+ 	 * The aforementioned logic is:
+ 	 * 1. The most recently installed filter is evaluated first (session)
+ 	 * 2. For a given filter, each syscall action is either the action
+ 	 *    value given in a syscall-specific rule, or the default action. 
+ 	 * 3. For any given syscall, the "first-seen action value of highest
+ 	 *    precedence" is applied. The precedence in order of high-to-low
+ 	 *    is: kill, error, log, allow.
+ 	 *
+ 	 * There are four combinations of the possible sets of rules to
+ 	 * consider:
+ 	 * g - global (postmaster)
+ 	 * s - session (backend)
+ 	 *
+ 	 * C1. Intersection of g + s
+ 	 * C2. In g, not in s
+ 	 * C3. In s, not in g
+ 	 * C4. Not in g or s
+ 	 *
+ 	 * C1 and C2 are handled by init_hash_from_bitmap()
+ 	 * and ovly_hash_from_bitmap(). C3 is handled by
+ 	 * ovly_hash_from_default(). C4 is covered by the final
+ 	 * "<default>" entry in the hash table.
+ 	 */
+ 	memset(&ctl, 0, sizeof(ctl));
+ 	ctl.keysize = sizeof(int);
+ 	ctl.entrysize = sizeof(seccompHashEntry);
+ 	seccompHash = hash_create("syscall rules", NUM_SECCOMP_RULES,
+ 							  &ctl, HASH_ELEM | HASH_BLOBS);
+ 
+ 	/*
+ 	 * Build up the hash table initially from the session filter.
+ 	 * We ensured no overlap of syscalls within a given filter in
+ 	 * load_seccomp_filter(), so it should be safe to just add
+ 	 * all the syscall numbers found in the 4 bitmap sets.
+ 	 */
+ 	init_hash_from_bitmap(s->kill, PG_SECCOMP_KILL, ssource, seccompHash);
+ 	init_hash_from_bitmap(s->error, PG_SECCOMP_ERROR, ssource, seccompHash);
+ 	init_hash_from_bitmap(s->log, PG_SECCOMP_LOG, ssource, seccompHash);
+ 	init_hash_from_bitmap(s->allow, PG_SECCOMP_ALLOW, ssource, seccompHash);
+ 
+ 	/*
+ 	 * Now overlay the global filter. Again we ensured no overlap
+ 	 * of syscalls within this filter in load_seccomp_filter(),
+ 	 * so it should be safe to just overlay all the syscall numbers
+ 	 * found in the 4 global bitmap sets.
+ 	 */
+ 	ovly_hash_from_bitmap(g->kill, PG_SECCOMP_KILL, gsource,
+ 						  sdef, ssource, seccompHash);
+ 	ovly_hash_from_bitmap(g->error, PG_SECCOMP_ERROR, gsource,
+ 						  sdef, ssource, seccompHash);
+ 	ovly_hash_from_bitmap(g->log, PG_SECCOMP_LOG, gsource,
+ 						  sdef, ssource, seccompHash);
+ 	ovly_hash_from_bitmap(g->allow, PG_SECCOMP_ALLOW, gsource,
+ 						  sdef, ssource, seccompHash);
+ 
+ 	/*
+ 	 * If rules from the session filter are not also explicitly
+ 	 * in the global filter, they must be compared against, and
+ 	 * possibly adjusted to, the global default action.
+ 	 */
+ 	gunion = bms_union(bms_union(bms_union(g->kill, g->error), g->log),
+ 					   g->allow);
+ 	sunion = bms_union(bms_union(bms_union(s->kill, s->error), s->log),
+ 					   s->allow);
+ 	ovly_hash_from_default(bms_difference(sunion, gunion),
+ 						   gdef, gsource, seccompHash);
+ 
+ 	/* create entry for the session default rule */
+ 	scr->syscallnum = -1;
+ 	scr->syscall = "<default>";
+ 	scr->rule_action = mdef;
+ 	scr->source = msource;
+ 	hentry = (seccompHashEntry *) hash_search(seccompHash,
+ 											  (const void *) &syscallnum,
+ 											  HASH_ENTER, NULL);
+ 	hentry->syscallnum = syscallnum;
+ 	hentry->scr_entry = scr;
+ 
+ 	/* Process the "session" results and fill the tuplestore */
+ 	hash_seq_init(&status, seccompHash);
+ 
+ 	while ((hentry = (seccompHashEntry *) hash_seq_search(&status)) != NULL)
+ 	{
+ 		char	   *buf;
+ 
+ 		memset(values, 0, sizeof(values));
+ 		memset(nulls, 0, sizeof(nulls));
+ 
+ 		scr = hentry->scr_entry;
+ 		buf = psprintf("%s->%s", scr->source,
+ 								 get_seccomp_opt_str(scr->rule_action));
+ 
+ 		values[0] = PointerGetDatum(cstring_to_text(scr->syscall));
+ 		values[1] = Int32GetDatum(scr->syscallnum);
+ 		values[2] = PointerGetDatum(cstring_to_text(buf));
+ 		values[3] = PointerGetDatum(cstring_to_text("session"));
+ 
+ 		/* shove row into tuplestore */
+ 		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ 	}
+ 
+ 	/*
+ 	 * Add rows for the "global" context. This is far simpler, since
+ 	 * we can simply iterate through the global bitmaps and do not
+ 	 * need to take care for rule precedence, etc., due to there
+ 	 * only being one filter (that we know about in any case).
+ 	 */
+ 	put_global_rules(g->kill, PG_SECCOMP_KILL, gsource, tupdesc, tupstore);
+ 	put_global_rules(g->error, PG_SECCOMP_ERROR, gsource, tupdesc, tupstore);
+ 	put_global_rules(g->log, PG_SECCOMP_LOG, gsource, tupdesc, tupstore);
+ 	put_global_rules(g->allow, PG_SECCOMP_ALLOW, gsource, tupdesc, tupstore);
+ 
+ 	/* create entry for the global default rule */
+ 	values[0] = PointerGetDatum(cstring_to_text("<default>"));
+ 	values[1] = Int32GetDatum(-1);
+ 
+ 	buf = psprintf("%s->%s", gsource, get_seccomp_opt_str(gdef));
+ 	values[2] = PointerGetDatum(cstring_to_text(buf));
+ 
+ 	values[3] = PointerGetDatum(cstring_to_text("global"));
+ 
+ 	/* shove row into tuplestore */
+ 	tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ 
+ #endif	/* USE_SECCOMP */
+ 
+ 	tuplestore_donestoring(tupstore);
+ 
+ 	/* Reset context */
+ 	MemoryContextSwitchTo(oldcontext);
+ 
+ 	return (Datum) 0;
+ }
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index 83c9514..1bf2e41 100644
*** a/src/backend/utils/init/miscinit.c
--- b/src/backend/utils/init/miscinit.c
***************
*** 29,34 ****
--- 29,38 ----
  #ifdef HAVE_UTIME_H
  #include <utime.h>
  #endif
+ #ifdef USE_SECCOMP
+ #include <seccomp.h>
+ #include <sys/prctl.h>
+ #endif
  
  #include "access/htup_details.h"
  #include "catalog/pg_authid.h"
***************
*** 36,41 ****
--- 40,46 ----
  #include "libpq/libpq.h"
  #include "mb/pg_wchar.h"
  #include "miscadmin.h"
+ #include "nodes/bitmapset.h"
  #include "pgstat.h"
  #include "postmaster/autovacuum.h"
  #include "postmaster/postmaster.h"
*************** pg_bindtextdomain(const char *domain)
*** 1617,1619 ****
--- 1622,2010 ----
  	}
  #endif
  }
+ 
+ /*-------------------------------------------------------------------------
+  *				seccomp filtering support
+  *-------------------------------------------------------------------------
+  */
+ 
+ /*
+  * GUC variables: lists of syscall names to be filtered at postmaster
+  * start and at backend start
+  */
+ 
+ const struct config_enum_entry seccomp_options[] = {
+ 	{"allow", PG_SECCOMP_ALLOW, false},
+ 	{"log", PG_SECCOMP_LOG, false},
+ 	{"error", PG_SECCOMP_ERROR, false},
+ 	{"kill", PG_SECCOMP_KILL, false},
+ 	{NULL, 0}
+ };
+ 
+ seccomp_filter *global_filter = NULL;
+ seccomp_filter *session_filter = NULL;
+ bool	seccomp_enabled = false;
+ int		global_syscall_default = PG_SECCOMP_ALLOW;
+ char   *global_syscall_allow_string = NULL;
+ char   *global_syscall_log_string = NULL;
+ char   *global_syscall_error_string = NULL;
+ char   *global_syscall_kill_string = NULL;
+ int		session_syscall_default = PG_SECCOMP_ALLOW;
+ char   *session_syscall_allow_string = NULL;
+ char   *session_syscall_log_string = NULL;
+ char   *session_syscall_error_string = NULL;
+ char   *session_syscall_kill_string = NULL;
+ 
+ #ifdef USE_SECCOMP
+ static bool apply_seccomp_list(scmp_filter_ctx	*ctx, const char *slist,
+ 							   uint32_t rule_action, uint32_t def_action,
+ 							   seccomp_filter *current_filter);
+ static const char *expand_seccomp_list(const char *slist, const char *glist,
+ 									   const char *saction);
+ static void set_filter_def_action(int default_action,
+ 								  seccomp_filter *current_filter,
+ 								  char *context);
+ #endif
+ 
+ /*
+  * Create and load seccomp filter for the requested context.
+  *
+  * Return false on error and let the caller decide what to do
+  * rather than throwing an ERROR (or FATAL) here.
+  */
+ bool
+ load_seccomp_filter(char *context)
+ {
+ #ifdef USE_SECCOMP
+ 	const char	   *allow_list = NULL;
+ 	const char	   *log_list = NULL;
+ 	const char	   *error_list = NULL;
+ 	const char	   *kill_list = NULL;
+ 	int				default_action;
+ 	uint32_t		def_action;
+ 	scmp_filter_ctx	ctx = NULL;
+ 	int				rc;
+ 	bool			result = true;
+ 	MemoryContext	oldcontext;
+ 	seccomp_filter *current_filter = NULL;
+ 
+ 	/* should not happen */
+ 	if (context == NULL)
+ 	{
+ 		ereport(WARNING, (errmsg("invalid seccomp context")));
+ 		return false;
+ 	}
+ 
+ 	/* if seccomp is disabled just return with success */
+ 	if (!seccomp_enabled)
+ 	{
+ 		ereport(LOG, (errmsg("seccomp disabled")));
+ 		return true;
+ 	}
+ 
+ 	/*
+ 	 * If the only character of the passed syscall_list is '*'
+ 	 * then use the global allow list. Only applies to children
+ 	 * of the postmaster.
+ 	 */
+ 	if (strcmp(context, "postmaster") != 0)
+ 	{
+ 		/* in a backend session */
+ 		/* we are going to need this later */
+ 		oldcontext = MemoryContextSwitchTo(TopMemoryContext);
+ 		session_filter = palloc0(sizeof(seccomp_filter));
+ 		session_filter->source = pstrdup("session");
+ 		MemoryContextSwitchTo(oldcontext);
+ 		current_filter = session_filter;
+ 
+ 		allow_list = expand_seccomp_list(session_syscall_allow_string,
+ 										 global_syscall_allow_string,
+ 										 "allow");
+ 		log_list = expand_seccomp_list(session_syscall_log_string,
+ 										 global_syscall_log_string,
+ 										 "log");
+ 		error_list = expand_seccomp_list(session_syscall_error_string,
+ 										 global_syscall_error_string,
+ 										 "error");
+ 		kill_list = expand_seccomp_list(session_syscall_kill_string,
+ 										 global_syscall_kill_string,
+ 										 "kill");
+ 
+ 		default_action = session_syscall_default;
+ 		/*
+ 		 * Fastpath: if the lists were all defaulted to their
+ 		 * respective global list, and the session value of
+ 		 * default_action is also the same as the global setting,
+ 		 * just exit with success immediately. This avoids creating
+ 		 * another identical seccomp bpf filter which will just
+ 		 * slow everything down for no particular reason.
+ 		 */
+ 		if (default_action == global_syscall_default &&
+ 				allow_list == global_syscall_allow_string &&
+ 				log_list == global_syscall_log_string &&
+ 				error_list == global_syscall_error_string &&
+ 				kill_list == global_syscall_kill_string)
+ 			return true;
+ 	}
+ 	else
+ 	{
+ 		/* in the postmaster */
+ 		/* we are going to need this later */
+ 		oldcontext = MemoryContextSwitchTo(TopMemoryContext);
+ 		global_filter = palloc0(sizeof(seccomp_filter));
+ 		global_filter->source = pstrdup("global");
+ 		MemoryContextSwitchTo(oldcontext);
+ 		current_filter = global_filter;
+ 
+ 		allow_list = global_syscall_allow_string;
+ 		log_list = global_syscall_log_string;
+ 		error_list = global_syscall_error_string;
+ 		kill_list = global_syscall_kill_string;
+ 		default_action = global_syscall_default;
+ 	}
+ 
+ 	/* Disable ptrace bybass */
+ 	rc = prctl(PR_SET_DUMPABLE, 0, 0, 0, 0);
+ 	if (rc < 0)
+ 	{
+ 		ereport(WARNING,
+ 				(ERRCODE_SYSTEM_ERROR,
+ 				 errmsg("seccomp could not set dumpable: %m")));
+ 		result = false;
+ 		goto out;
+ 	}
+ 
+ 	/* set the seccomp default action */
+ 	if (default_action == PG_SECCOMP_ERROR)
+ 		def_action = SCMP_ACT_ERRNO(EACCES);
+ 	else if (default_action == PG_SECCOMP_KILL)
+ 		def_action = SCMP_ACT_KILL;
+ 	else if (default_action == PG_SECCOMP_LOG)
+ 		def_action = SCMP_ACT_LOG;
+ 	else if (default_action == PG_SECCOMP_ALLOW)
+ 		def_action = SCMP_ACT_ALLOW;
+ 	else
+ 	{
+ 		/* unknown enforce action type */
+ 		ereport(WARNING,
+ 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 				 errmsg("seccomp default action action unknown")));
+ 		result = false;
+ 		goto out;
+ 	}
+ 	/* preserve and log the setting */
+ 	set_filter_def_action(default_action, current_filter, context);
+ 
+ 	/* Initialize seccomp with default action */
+ 	ctx = seccomp_init(def_action);
+ 	if (ctx == NULL)
+ 	{
+ 		ereport(WARNING, (errcode(ERRCODE_OUT_OF_MEMORY),
+ 						  errmsg("out of memory")));
+ 		result = false;
+ 		goto out;
+ 	}
+ 
+ 	/*
+ 	 * By default, libseccomp will set up audit logging
+ 	 * such that actions KILL and LOG will get audit records,
+ 	 * however ERRNO will not. Arrange to have all not-allowed
+ 	 * syscalls logged instead.
+ 	 */
+ 	rc = seccomp_attr_set(ctx, SCMP_FLTATR_CTL_LOG, 1);
+ 	if (rc != 0)
+ 	{
+ 		ereport(WARNING,
+ 				(errcode(ERRCODE_SYSTEM_ERROR),
+ 				 errmsg("seccomp failed to set audit actions")));
+ 		result = false;
+ 		goto out;
+ 	}
+ 
+ 	if (!
+ 		 (apply_seccomp_list(&ctx, allow_list, SCMP_ACT_ALLOW,
+ 							 def_action, current_filter) &&
+ 		  apply_seccomp_list(&ctx, log_list, SCMP_ACT_LOG,
+ 							 def_action, current_filter) &&
+ 		  apply_seccomp_list(&ctx, error_list, SCMP_ACT_ERRNO(EACCES),
+ 							 def_action, current_filter) &&
+ 		  apply_seccomp_list(&ctx, kill_list, SCMP_ACT_KILL,
+ 							 def_action, current_filter)))
+ 	{
+ 		result = false;
+ 		goto out;
+ 	}
+ 
+ 	/*
+ 	 * Although libseccomp will silently throw away repeated filter
+ 	 * rules against the same syscall (unless arguments are checked,
+ 	 * which we are not supporting here), it can lead to confusing
+ 	 * results, so disallow that here.
+ 	 */
+ 	if (bms_overlap(current_filter->allow, current_filter->log) ||
+ 		bms_overlap(current_filter->error, current_filter->kill) ||
+ 		bms_overlap(bms_union(current_filter->allow, current_filter->log),
+ 					bms_union(current_filter->error, current_filter->kill)))
+ 	{
+ 		ereport(WARNING,
+ 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 				 errmsg("seccomp failed due to overlapping rule sets")));
+ 		result = false;
+ 		goto out;
+ 	}
+ 
+ 	/* Finally, actually load the filter */
+ 	rc = seccomp_load(ctx);
+ 	if (rc != 0)
+ 	{
+ 		ereport(WARNING,
+ 				(errcode(ERRCODE_SYSTEM_ERROR),
+ 				 errmsg("seccomp failed to load rule set")));
+ 		result = false;
+ 		goto out;
+ 	}
+ 
+ out:
+ 	/* safe to release if NULL/NIL */
+ 	seccomp_release(ctx);
+ 
+ 	return result;
+ #else
+ 	return false;
+ #endif
+ }
+ 
+ #ifdef USE_SECCOMP
+ static bool
+ apply_seccomp_list(scmp_filter_ctx	*ctx, const char *slist,
+ 				   uint32_t rule_action, uint32_t def_action,
+ 				   seccomp_filter *current_filter)
+ {
+ 	char		   *rawstring = NULL;
+ 	List		   *elemlist = NIL;
+ 	ListCell	   *l;
+ 	bool			result = true;
+ 	MemoryContext	oldcontext;
+ 
+ 	/* 
+ 	 * libseccomp disallows the case where individual syscall rules
+ 	 * are created with the same as the default action. Therefore,
+ 	 * be careful not to add those rules to the filter we are creating.
+ 	 */
+ 	if (rule_action == def_action)
+ 		return true;
+ 
+ 	/* Need a modifiable copy */
+ 	rawstring = pstrdup(slist);
+ 
+ 	/* Parse string into list of syscalls */
+ 	if (!SplitIdentifierString(rawstring, ',', &elemlist))
+ 	{
+ 		result = false;
+ 		goto out;
+ 	}
+ 
+ 	/* add syscall specific rules to the filter */
+ 	foreach(l, elemlist)
+ 	{
+ 		char   *cursyscall = (char *) lfirst(l);
+ 		int		syscallnum;
+ 		int		rc;
+ 
+ 		/*
+ 		 * Resolve the syscall name to its number on the current arch.
+ 		 * This should have already been validated by the GUC
+ 		 * check function.
+ 		 */
+ 		syscallnum = seccomp_syscall_resolve_name(cursyscall);
+ 		if (syscallnum < 0)
+ 		{
+ 			/* should not happen */
+ 			ereport(WARNING,
+ 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 					 errmsg("seccomp failed to resolve: syscall \"%s\"",
+ 							cursyscall)));
+ 			result = false;
+ 			goto out;
+ 		}
+ 		else
+ 		{
+ 			rc = seccomp_rule_add(*ctx, rule_action, syscallnum, 0);
+ 			if (rc != 0)
+ 			{
+ 				/* should not be reachable */
+ 				ereport(WARNING,
+ 						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 						 errmsg("seccomp failed to add rule: syscall \"%s\", %d",
+ 								 cursyscall, syscallnum)));
+ 				result = false;
+ 				goto out;
+ 			}
+ 			oldcontext = MemoryContextSwitchTo(TopMemoryContext);
+ 
+ 			if (rule_action == SCMP_ACT_ALLOW)
+ 				current_filter->allow = bms_add_member(current_filter->allow,
+ 													   syscallnum);
+ 			else if (rule_action == SCMP_ACT_LOG)
+ 				current_filter->log = bms_add_member(current_filter->log,
+ 													   syscallnum);
+ 			else if (rule_action == SCMP_ACT_ERRNO(EACCES))
+ 				current_filter->error = bms_add_member(current_filter->error,
+ 													   syscallnum);
+ 			else if (rule_action == SCMP_ACT_KILL)
+ 				current_filter->kill = bms_add_member(current_filter->kill,
+ 													   syscallnum);
+ 
+ 			MemoryContextSwitchTo(oldcontext);
+ 		}
+ 	}
+ 
+ out:
+ 	/* safe to release if still NIL */
+ 	list_free(elemlist);
+ 
+ 	/* but pfree is not */
+ 	if (rawstring)
+ 		pfree(rawstring);
+ 
+ 	return result;
+ }
+ 
+ static const char*
+ expand_seccomp_list(const char *slist, const char *glist,
+ 					const char *saction)
+ {
+ 	
+ 	if (slist && strlen(slist) == 1 && slist[0] == '*')
+ 	{
+ 		/* use the global list as promised */
+ 		ereport(LOG,
+ 				(errmsg("seccomp \"%s\" list inherited from postmaster", saction)));
+ 
+ 		return glist;
+ 	}
+ 	else
+ 		return slist;
+ }
+ 
+ static void
+ set_filter_def_action(int default_action, seccomp_filter *current_filter,
+ 					  char *context)
+ {
+ 	const struct config_enum_entry *entry;
+ 
+ 	current_filter->def = default_action;
+ 	/* stringify the enforcement action levels */
+ 	for (entry = seccomp_options; entry->name; entry++)
+ 	{
+ 		if (entry->val == default_action)
+ 		{
+ 			current_filter->def_str = entry->name;
+ 			break;
+ 		}
+ 	}
+ 	ereport(LOG,
+ 			(errmsg("seccomp default action set to \"%s\": context \"%s\"",
+ 					current_filter->def_str, context)));
+ }
+ #endif /* USE_SECCOMP */
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 43b9f17..aac1940 100644
*** a/src/backend/utils/init/postinit.c
--- b/src/backend/utils/init/postinit.c
*************** InitPostgres(const char *in_dbname, Oid
*** 1056,1061 ****
--- 1056,1076 ----
  	/* Process pg_db_role_setting options */
  	process_settings(MyDatabaseId, GetSessionUserId());
  
+ #ifdef USE_SECCOMP
+ 	/* If seccomp filtering is requested, do the backend lockdown */
+ 	if (!bootstrap &&
+ 		!IsAutoVacuumWorkerProcess() &&
+ 		 IsUnderPostmaster)
+ 	{
+ 		if(!load_seccomp_filter("session"))
+ 		{
+ 			ereport(FATAL,
+ 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 					 errmsg("failed to load session seccomp filter")));
+ 		}
+ 	}
+ #endif
+ 
  	/* Apply PostAuthDelay as soon as we've read all options */
  	if (PostAuthDelay > 0)
  		pg_usleep(PostAuthDelay * 1000000L);
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 90ffd89..8b548f1 100644
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
*************** extern const struct config_enum_entry ar
*** 478,483 ****
--- 478,484 ----
  extern const struct config_enum_entry recovery_target_action_options[];
  extern const struct config_enum_entry sync_method_options[];
  extern const struct config_enum_entry dynamic_shared_memory_options[];
+ extern const struct config_enum_entry seccomp_options[];
  
  /*
   * GUC option variables that are exported from this module
*************** static struct config_bool ConfigureNames
*** 1952,1957 ****
--- 1953,1968 ----
  		NULL, NULL, NULL
  	},
  
+ 	{
+ 		{"seccomp", PGC_POSTMASTER, RESOURCES_KERNEL,
+ 			gettext_noop("Turns on seccomp syscall enforcement."),
+ 			NULL
+ 		},
+ 		&seccomp_enabled,
+ 		false,
+ 		NULL, NULL, NULL
+ 	},
+ 
  	/* End-of-list marker */
  	{
  		{NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL
*************** static struct config_string ConfigureNam
*** 4199,4204 ****
--- 4210,4303 ----
  		NULL, NULL, NULL
  	},
  
+ 	{
+ 		{"global_syscall_allow", PGC_POSTMASTER, RESOURCES_KERNEL,
+ 			gettext_noop("Seccomp global syscall allow list."),
+ 			NULL,
+ 			GUC_LIST_INPUT | GUC_SUPERUSER_ONLY
+ 		},
+ 		&global_syscall_allow_string,
+ 		"",
+ 		check_global_syscall_list, NULL, NULL
+ 	},
+ 
+ 	{
+ 		{"global_syscall_log", PGC_POSTMASTER, RESOURCES_KERNEL,
+ 			gettext_noop("Seccomp global syscall log list."),
+ 			NULL,
+ 			GUC_LIST_INPUT | GUC_SUPERUSER_ONLY
+ 		},
+ 		&global_syscall_log_string,
+ 		"",
+ 		check_global_syscall_list, NULL, NULL
+ 	},
+ 
+ 	{
+ 		{"global_syscall_error", PGC_POSTMASTER, RESOURCES_KERNEL,
+ 			gettext_noop("Seccomp global syscall error list."),
+ 			NULL,
+ 			GUC_LIST_INPUT | GUC_SUPERUSER_ONLY
+ 		},
+ 		&global_syscall_error_string,
+ 		"",
+ 		check_global_syscall_list, NULL, NULL
+ 	},
+ 
+ 	{
+ 		{"global_syscall_kill", PGC_POSTMASTER, RESOURCES_KERNEL,
+ 			gettext_noop("Seccomp global syscall kill list."),
+ 			NULL,
+ 			GUC_LIST_INPUT | GUC_SUPERUSER_ONLY
+ 		},
+ 		&global_syscall_kill_string,
+ 		"",
+ 		check_global_syscall_list, NULL, NULL
+ 	},
+ 
+ 	{
+ 		{"session_syscall_allow", PGC_SUSET, RESOURCES_KERNEL,
+ 			gettext_noop("Seccomp backend session syscall allow list."),
+ 			NULL,
+ 			GUC_LIST_INPUT | GUC_SUPERUSER_ONLY
+ 		},
+ 		&session_syscall_allow_string,
+ 		"*",
+ 		check_session_syscall_list, NULL, NULL
+ 	},
+ 
+ 	{
+ 		{"session_syscall_log", PGC_SUSET, RESOURCES_KERNEL,
+ 			gettext_noop("Seccomp backend session syscall log list."),
+ 			NULL,
+ 			GUC_LIST_INPUT | GUC_SUPERUSER_ONLY
+ 		},
+ 		&session_syscall_log_string,
+ 		"*",
+ 		check_session_syscall_list, NULL, NULL
+ 	},
+ 
+ 	{
+ 		{"session_syscall_error", PGC_SUSET, RESOURCES_KERNEL,
+ 			gettext_noop("Seccomp backend session syscall error list."),
+ 			NULL,
+ 			GUC_LIST_INPUT | GUC_SUPERUSER_ONLY
+ 		},
+ 		&session_syscall_error_string,
+ 		"*",
+ 		check_session_syscall_list, NULL, NULL
+ 	},
+ 
+ 	{
+ 		{"session_syscall_kill", PGC_SUSET, RESOURCES_KERNEL,
+ 			gettext_noop("Seccomp backend session syscall allow kill."),
+ 			NULL,
+ 			GUC_LIST_INPUT | GUC_SUPERUSER_ONLY
+ 		},
+ 		&session_syscall_kill_string,
+ 		"*",
+ 		check_session_syscall_list, NULL, NULL
+ 	},
+ 
  	/* End-of-list marker */
  	{
  		{NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL, NULL
*************** static struct config_enum ConfigureNames
*** 4537,4542 ****
--- 4636,4661 ----
  		NULL, NULL, NULL
  	},
  
+ 	{
+ 		{"global_syscall_default", PGC_POSTMASTER, RESOURCES_KERNEL,
+ 			gettext_noop("Seccomp global syscall default action."),
+ 			NULL
+ 		},
+ 		&global_syscall_default,
+ 		PG_SECCOMP_ALLOW, seccomp_options,
+ 		NULL, NULL, NULL
+ 	},
+ 
+ 	{
+ 		{"session_syscall_default", PGC_SUSET, RESOURCES_KERNEL,
+ 			gettext_noop("Seccomp beckend session syscall default action."),
+ 			NULL
+ 		},
+ 		&session_syscall_default,
+ 		PG_SECCOMP_ALLOW, seccomp_options,
+ 		NULL, NULL, NULL
+ 	},
+ 
  	/* End-of-list marker */
  	{
  		{NULL, 0, 0, NULL, NULL}, NULL, 0, NULL, NULL, NULL, NULL
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 39fc787..82187c1 100644
*** a/src/backend/utils/misc/postgresql.conf.sample
--- b/src/backend/utils/misc/postgresql.conf.sample
***************
*** 154,159 ****
--- 154,177 ----
  
  #max_files_per_process = 1000		# min 25
  					# (change requires restart)
+ #seccomp = off				# use seccomp
+ 					# (change requires restart)
+ 
+ #global_syscall_default = allow		# postmaster default syscall action:
+ 					# allow, log, error, kill
+ #global_syscall_allow = ''			# postmaster syscall allow list
+ #global_syscall_log = ''			# postmaster syscall log list
+ #global_syscall_error = ''			# postmaster syscall error list
+ #global_syscall_kill = ''			# postmaster syscall kill list
+ 					# (global_syscall* change requires restart)
+ 
+ #session_syscall_default = allow	# session default syscall action:
+ 					# allow, log, error, kill
+ #session_syscall_allow = '*'		# backend session syscall allow list
+ #session_syscall_log = '*'			# backend session syscall log list
+ #session_syscall_error = '*'		# backend session syscall error list
+ #session_syscall_kill = '*'		# backend session syscall kill list
+ 					# session_syscall* default '*' = use global list
  
  # - Cost-Based Vacuum Delay -
  
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index cf1f409..a32522e 100644
*** a/src/include/catalog/pg_proc.dat
--- b/src/include/catalog/pg_proc.dat
***************
*** 10678,10683 ****
--- 10678,10690 ----
    proallargtypes => '{oid,text,int8,timestamptz}', proargmodes => '{i,o,o,o}',
    proargnames => '{tablespace,name,size,modification}',
    prosrc => 'pg_ls_tmpdir_1arg' },
+ { oid => '8657', descr => 'get current effective seccomp filter actions',
+   proname => 'pg_get_seccomp_filter', prorows => '100', proretset => 't',
+   provolatile => 's', proparallel => 'r', prorettype => 'record',
+   proargtypes => '', proallargtypes => '{text,int4,text,text}',
+   proargmodes => '{o,o,o,o}',
+   proargnames => '{syscall,syscallnum,filter_action,context}',
+   prosrc => 'pg_get_seccomp_filter' },
  
  # hash partitioning constraint function
  { oid => '5028', descr => 'hash partition CHECK constraint',
diff --git a/src/include/commands/variable.h b/src/include/commands/variable.h
index 5f43414..58cf427 100644
*** a/src/include/commands/variable.h
--- b/src/include/commands/variable.h
*************** extern void assign_session_authorization
*** 34,38 ****
--- 34,40 ----
  extern bool check_role(char **newval, void **extra, GucSource source);
  extern void assign_role(const char *newval, void *extra);
  extern const char *show_role(void);
+ extern bool check_global_syscall_list(char **newval, void **extra, GucSource source);
+ extern bool check_session_syscall_list(char **newval, void **extra, GucSource source);
  
  #endif							/* VARIABLE_H */
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index bc6e03f..1e6745f 100644
*** a/src/include/miscadmin.h
--- b/src/include/miscadmin.h
***************
*** 26,31 ****
--- 26,32 ----
  #include <signal.h>
  
  #include "datatype/timestamp.h" /* for TimestampTz */
+ #include "nodes/bitmapset.h"	/* for seccomp */
  #include "pgtime.h"				/* for pg_time_t */
  
  
*************** extern void ChangeToDataDir(void);
*** 333,338 ****
--- 334,341 ----
  extern void SwitchToSharedLatch(void);
  extern void SwitchBackToLocalLatch(void);
  
+ extern bool load_seccomp_filter(char *context);
+ 
  /* in utils/misc/superuser.c */
  extern bool superuser(void);	/* current user is superuser */
  extern bool superuser_arg(Oid roleid);	/* given user is superuser */
*************** extern void process_session_preload_libr
*** 447,452 ****
--- 450,485 ----
  extern void pg_bindtextdomain(const char *domain);
  extern bool has_rolreplication(Oid roleid);
  
+ typedef struct seccomp_filter
+ {
+ 	char		   *source;
+ 	int				def;
+ 	const char	   *def_str;
+ 	Bitmapset	   *allow;
+ 	Bitmapset	   *log;
+ 	Bitmapset	   *error;
+ 	Bitmapset	   *kill;
+ } seccomp_filter;
+ extern seccomp_filter *global_filter;
+ extern seccomp_filter *session_filter;
+ extern bool seccomp_enabled;
+ extern int global_syscall_default;
+ extern int session_syscall_default;
+ extern char *global_syscall_allow_string;
+ extern char *global_syscall_log_string;
+ extern char *global_syscall_error_string;
+ extern char *global_syscall_kill_string;
+ extern char *session_syscall_allow_string;
+ extern char *session_syscall_log_string;
+ extern char *session_syscall_error_string;
+ extern char *session_syscall_kill_string;
+ /* seccomp enforce actions in increasing order of precedence */
+ #define PG_SECCOMP_ALLOW    0  /* allow */
+ #define PG_SECCOMP_LOG      1  /* log */
+ #define PG_SECCOMP_ERROR    2  /* permission denied error */
+ #define PG_SECCOMP_KILL     3  /* kill process */
+ 
+ 
  /* in access/transam/xlog.c */
  extern bool BackupInProgress(void);
  extern void CancelBackup(void);
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index d876926..dc7fdaf 100644
*** a/src/include/pg_config.h.in
--- b/src/include/pg_config.h.in
***************
*** 353,358 ****
--- 353,361 ----
  /* Define if you have a function readline library */
  #undef HAVE_LIBREADLINE
  
+ /* Define to 1 if you have the `seccomp' library (-lseccomp). */
+ #undef HAVE_LIBSECCOMP
+ 
  /* Define to 1 if you have the `selinux' library (-lselinux). */
  #undef HAVE_LIBSELINUX
  
***************
*** 935,940 ****
--- 938,946 ----
  /* Define to 1 to build with PAM support. (--with-pam) */
  #undef USE_PAM
  
+ /* Define to 1 to build with seccomp support. (--with-seccomp) */
+ #undef USE_SECCOMP
+ 
  /* Define to 1 to use software CRC-32C implementation (slicing-by-8). */
  #undef USE_SLICING_BY_8_CRC32C
  
