FreeBSD 13.0-RELEASE is out, and one of the new features is W^X memory mapping policy for user processes. This feature is also naturally available for users of 13.0-STABLE and 14.0-CURRENT.

Enable the feature, i.e. disallow pages to be executable and writable, by adding these lines to /etc/sysctl.conf:

kern.elf32.allow_wx=0
kern.elf64.allow_wx=0

For immediate effect, run these commands as root:

sysctl kern.elf32.allow_wx=0
sysctl kern.elf64.allow_wx=0

Some executables can’t live with these restrictions. It’s possible to mark an executable as needing its pages to be both executable and writable:

# elfctl -e +wxneeded /usr/local/bin/synth
# elfctl -e +wxneeded /usr/local/lib/libreoffice/program/soffice.bin

Checking the results, reveals:

$ elfctl /usr/local/bin/synth
File '/usr/local/bin/synth' features:
noaslr          'Disable ASLR' is unset.
noprotmax       'Disable implicit PROT_MAX' is unset.
nostackgap      'Disable stack gap' is unset.
wxneeded        'Requires W+X mappings' is set.
la48            'amd64: Limit user VA to 48bit' is unset.
noaslrstkgap    'Disable ASLR stack gap' is unset.

$ elfctl /usr/local/lib/libreoffice/program/soffice.bin
File '/usr/local/lib/libreoffice/program/soffice.bin' features:
noaslr          'Disable ASLR' is unset.
noprotmax       'Disable implicit PROT_MAX' is unset.
nostackgap      'Disable stack gap' is unset.
wxneeded        'Requires W+X mappings' is set.
la48            'amd64: Limit user VA to 48bit' is unset.
noaslrstkgap    'Disable ASLR stack gap' is unset.

Note, executables produced by lang/fpc 3.2.0 doesn’t carry the NT_FREEBSD_FEATURE_CTL note section.

A few ports are unable to be built or live under such restrictions. So far, the list includes:

  • editors/libreoffice
  • java/openjdk8
  • lang/fpc
  • lang/mono
  • lang/python3x
  • ports-mgmt/synth

My shell script (not shown here) for building packages re-enables kern.elf64.allow_wx, setting it to 1, allowing pages to be writable and executable, before running ports-mgmt/synth. An exit handler in the same script will disable kern.elf64.allow_wx, setting it to 0, disallowing pages to be writable and executable, once all packages are built.

Here’s a shell script I concocted to mark the /usr/local/bin/synth binary as needing writable and executable pages:

#!/bin/sh
#set -x
set -eu -o pipefail

# A quick reminder for myself:

# Disable/disallow writable and executable (wx) pages system-wide:
# sysctl kern.elf64.allow_wx=0
# sysctl kern.elf32.allow_wx=0

# Enable/allow writable and executable (wx) pages system-wide:
# sysctl kern.elf64.allow_wx=1
# sysctl kern.elf32.allow_wx=1

ELFCTL="elfctl"
GREP="grep"
SYNTH="/usr/local/bin/synth"

if ${ELFCTL} ${SYNTH} | ${GREP} "wxneeded" | ${GREP} -q "is set"; then
  echo "${0}: wxneeded is already set on ${SYNTH}."
  exit
fi

echo "${0}: elfctl(1) feature flags on ${SYNTH} before any changes:"
${ELFCTL} ${SYNTH}
echo

echo "${0}: Setting wxneeded on ${SYNTH} ..."
${ELFCTL} -e +wxneeded ${SYNTH}
echo "... done."
echo

echo "${0}: elfctl(1) feature flags afterwards:"
${ELFCTL} ${SYNTH}

# EOF

On a related note, if you use ASLR, then Firefox must be allowed to run without ASLR.

#!/bin/sh
#set -x
set -eu -o pipefail

# A quick reminder for myself:

# Disable aslr system-wide:
# sysctl kern.elf64.aslr.enable=0
# sysctl kern.elf32.aslr.enable=0

# Enable aslr system-wide:
# sysctl kern.elf64.aslr.enable=1
# sysctl kern.elf32.aslr.enable=1

ELFCTL="elfctl"
GREP="grep"
FIREFOX="/usr/local/lib/firefox/firefox"

if ${ELFCTL} ${FIREFOX} | ${GREP} "noaslr " | ${GREP} -q "is set"; then
  echo "${0}: noaslr is already set on ${FIREFOX}."
  exit
fi

echo "${0}: elfctl(1) feature flags on ${FIREFOX} before any changes:"
${ELFCTL} ${FIREFOX}
echo

echo "${0}: Setting noaslr on ${FIREFOX} ..."
${ELFCTL} -e +noaslr ${FIREFOX}
echo "... done."
echo

echo "${0}: elfctl(1) feature flags afterwards:"
${ELFCTL} ${FIREFOX}

# EOF
  1. I’m confused. You say to immediately enable set them to 0 in rc.conf then later in the shell script you set them to 1 to enable?

    • When these two sysctls are set to 1, we allow pages to be both writable and executable. When set to 0, we disallow pages to be both writable and executable. And it’s /etc/sysctl.conf, not /etc/rc.conf.

      Some software can’t cope with this limitation. The Ada runtime of ports-mgmt/synth is one such example. The executables produced by lang/fpc 3.2.0 doesn’t even carry the NT_FREEBSD_FEATURE_CTL note section, and such executables doesn’t like it when W xor X = 1.

      I have seen things go awry when running make index, make config-recursive, and make fetch-recursive in the ports tree, when these two sysctls are set to 0. This happens for sure when any of the three commands above hits lang/fpc and its subsidiaries.

      I reworded my blog post, and hopefully it’s more readable now.

      Thank you for stopping by and leaving a comment.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>