Page MenuHomeFreeBSD

rc.subr: serialize service-jail creation to avoid a create-create race
Needs ReviewPublic

Authored by bofh on Tue, Sep 1, 2:09 PM.

Details

Summary

run_rc_command() has two svcj "start" implementations (the
$_cmd-override branch and teh default command/precmd branch below it.
nginx uses the latter). Both call jail -c ... name=svcj-${name}
unconditionally, wiht nothing to stop two concurrent starts of the
same service from both attempting it. The loser gets jail(8)'s hard
"already exists" failure. "restart" already already guards against
this (jls -j svcj-${name} jid before acting) but "start" never did, in
either branch.

A bare precheck isn't enouhg as two starts close enough together both
pass the check before either has created the jail, and both still
race race into jail -c. The fix is to wrap the recheck-then-create in a
lockf(1) lock per service jail.

The lock/create body is passed to /bin/sh -c via positional params
($1=name, "$@"=the jail -c invocation) rahter than interpolated into
the quoted script, so $_svcj_generic_params/$_svcj_cmd_options keep
thier existing word-splitting and nothing needs re-quoting.

Test Plan

given a jail <j> with nginx_svcj=YES already running nginx:

jexec <j> service nginx stop
jexec <j> service nginx start & jexec <j> service nginx start &
wait

Before this patch: one of teh two reliably fails with jail: "<jid>" already exists.

After this patch: both succeed. Only one svcj-nginx jail is ever creaetd (jls -n name | grep svcj-nginx shows a single line).
The secodn invocation blocks on the new lock, then finds the jail already up and never calls jail -c at all.

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Skipped
Unit
Tests Skipped
Build Status
Buildable 76380
Build 73263: arc lint + arc unit