This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

What to do when glibc environment variables or tunables conflict?


Explicit use of LD_BIND_NOW=1 or LD_DEBUG=unused disables ld audit
because both uses set GLRO(dl_lazy) to zero and disable lazy binding
entirely. The ld audit interface requires the ld lazy binding hook
to perform various auditing activities. In the future, after refactoring,
we should be able to support all of LD_AUDIT with the exception of PLT
enter/exit (which has questionable value).

What should happen here?

Which env vars should override other env vars?

Should env vars override options built into a binary?

For example if the auditor is enabled via DT_AUDIT (a feature which
we are going to add) should LD_BIND_NOW or LD_DEBUG=unused disable it?

I would like to suggest the following axioms:

(A1) Consistent results should be produced regardless of env var order
     or tunable order.

(A2) Where multiple variables conflict we should pick a winner and consider
     hard failing where users require a specific semantic behaviour.

(A3) Where conflict exists the dynamic loader prints a warning for an env var
     that is ignored e.g. clear and consistent visibility for users.

(A4) Give preference to any executable-encoded choices over environment variables.

Picking a winner:

Notes:
- When "print a warning" is written this can mean that we only print something
  if LD_DEBUG=all is set or something that matches the warnings we would like
  to print.

(a) Auditing is functionally required since it can modify load search
    paths and symbol binding for applications.

(b) Immediate binding is generally an optimization or a workaround.
    - Enabled unconditionally by some architectures for certain symbol types
      e.g. AArch64 VPCS, where this is a compromise between ABI and
      performance. The use of any env var or executable-encoded option should
      not disable this architecture-specific choice.

There are 5 state variables:
LD_DEBUG=unused / LD_DEBUG=
LD_BIND_NOW=1 / LD_BIND_NOW=
LD_AUDIT=... / LD_AUDIT=
DT_AUDIT / No DT_AUDIT
DT_FLAGS BIND_NOW / No DT_FLAGS BIND_NOW

Which is 32 states, but we can collapse the LD_DEBUG state because it is
a debug state. All subsequent states, 16 of them, are listed below, some
of which conflict and some which don't.

(1) LD_DEBUG=unused / <All other states>, LD_DEBUG= / <All other states>

    LD_DEBUG=unused should print a warning and be disabled if BIND_NOW
    (builtin or env var) or ld audit is in use (builtin or env var)

(2) LD_BIND_NOW=1 / LD_AUDIT= / DT_AUDIT / No DT_FLAGS BIND_NOW

    LD_BIND_NOW=1 should print a warning and be disabled if DT_AUDIT is
    specified in the binary or any initially loaded shared objects.
    - Requires you inspect dynsym of all loaded objects in dep tree.

(3) LD_BIND_NOW=1 / LD_AUDIT= / No DT_AUDIT / No DT_FLAGS BIND_NOW
    LD_BIND_NOW=1 / LD_AUDIT= / No DT_AUDIT / DT_FLAGS BIND_NOW

    LD_BIND_NOW=1 should work as expected if the binary or any initially
    loaded shared objects don't have DT_AUDIT.
    - If DT_AUDIT appears in an object that is dlopen'd then we should fail
      to load such an object since we can't honour the encoded request.

(4) Conflict:
    LD_BIND_NOW= / LD_AUDIT= / DT_AUDIT / DT_FLAGS BIND_NOW
    LD_BIND_NOW= / LD_AUDIT=... / DT_AUDIT / DT_FLAGS BIND_NOW
    LD_BIND_NOW=1 / LD_AUDIT= / DT_AUDIT / DT_FLAGS BIND_NOW
    LD_BIND_NOW=1 / LD_AUDIT=... / DT_AUDIT / DT_FLAGS BIND_NOW

    No conflict:
    LD_BIND_NOW= / LD_AUDIT= / No DT_AUDIT / No DT_FLAGS BIND_NOW
    LD_BIND_NOW= / LD_AUDIT= / No DT_AUDIT / DT_FLAGS BIND_NOW
    LD_BIND_NOW= / LD_AUDIT= / DT_AUDIT / No DT_FLAGS BIND_NOW

    If -Wl,-z,now and -Wl,--audit,AUDITLIB are used together then the
    dynamic loader should honour DT_AUDIT and print a warning that
    BIND_NOW is disabled (see (a)).

(5) LD_BIND_NOW= / LD_AUDIT=... / No DT_AUDIT / DT_FLAGS BIND_NOW
    LD_BIND_NOW=1 / LD_AUDIT=... / No DT_AUDIT / DT_FLAGS BIND_NOW

    LD_AUDIT and DT_FLAGS BIND_NOW are used together then the dynamic
    loader should honour BIND_NOW and print a warning that LD_AUDIT is
    disabled (see (A4)).

(6) Conflict:
    LD_BIND_NOW=1 / LD_AUDIT=... / No DT_AUDIT / No DT_FLAGS BIND_NOW
    LD_BIND_NOW=1 / LD_AUDIT=... / DT_AUDIT / No DT_FLAGS BIND_NOW

    No conflict:
    LD_BIND_NOW= / LD_AUDIT=... / No DT_AUDIT / No DT_FLAGS BIND_NOW
    LD_BIND_NOW= / LD_AUDIT=... / DT_AUDIT / No DT_FLAGS BIND_NOW

    LD_AUDIT and no DT_FLAGS BIND_NOW during initial startup should allow
    ld audit to operate.
    - If subsequent dlopen requires BIND_NOW. We should load the requested
    object but print a warning that BIND_NOW is disabled due to auditing.
    - If LD_BIND_NOW=1 is specified we should print a warning that it is
      ignored.

Notes:
- When LD_AUDIT and DT_AUDIT are specified the list of auditors is the
  concatenation of both auditor lists.
- Given that LD_AUDIT is rare, and that when present it's usually a required
  feature for the purposes of auditing or search path manipulation, we pick
  it over LD_BIND_NOW / DT_FLAGS BIND_NOW. It would seem like we shouldn't
  because LD_BIND_NOW is important for security, but in the vast majority of
  the cases we should be able to easily enable BIND_NOW without conflict with
  an auditor. We pick the auditor because it may have an even larger security
  impact like disallowing binaries from certain paths to be loaded e.g.
  production vs. debug shared objects.

-- 
Cheers,
Carlos.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]