Commit graph

64 commits

Author SHA1 Message Date
Ryan Lee
4286423fe2 parser: hfa.cc debug promt->prompt typofix
Signed-off-by: Ryan Lee <ryan.lee@canonical.com>
2025-02-18 11:53:35 -08:00
John Johansen
1ebd991155 parser: change priority so that it accumulates based on permissions
The current behavior of priority rules can be non-intuitive with
higher priority rules completely overriding lower priority rules even in
permissions not held in common. This behavior does have use cases but
its can be very confusing, and does not normal policy behavior

Eg.
  priority=0 allow r /**,
  priority=1 deny  w /**,

will result in no allowed permissions even though the deny rule is
only removing the w permission, beause the higher priority rule
completely over ride lower priority permissions sets (including
none shared permissions).

Instead move to tracking the priority at a per permission level. This
allows the w permission to still override at priority 1, while the
read permission is allowed at priority 0.

The final constructed state will still drop priority for the final
permission set on the state.

Note: this patch updates the equality tests for the cases where
the complete override behavior was being tested for.

The complete override behavior will be reintroduced in a future
patch with a keyword extension, enabling that behavior to be used
for ordered blocks etc.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-02-06 11:02:20 -08:00
John Johansen
cc31a0da22 parser: drop priority from state permissions
The priority field is only used during state construction, and can
even prevent later optimizations like minimization. The parser already
explcitily clears the states priority field as part of the last thing
done during construction so it doesn't prevent minimization
optimizations.

This means the state priority not only wastes storage because it is
unused post construction but if used it could introduce regressions,
or other issues.

The change to the minimization tests just removes looking for the
priority field that is no longer reported.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-02-06 10:53:19 -08:00
John Johansen
9221d291ec parser: stop using dynamic_cast for prompt permission calculations
Like was done for the other MatchFlags switch to using a node type
instead of dynamic_cast as this will result in a performance
improvement.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-02-06 08:34:02 -08:00
John Johansen
72f9952a5f Merge parser: add a hfa dump that matches the renumbered chfa
Construction of the chfa can reorder states from what the numbering
given during the hfa constuctions because of reordering for better
compression, dead state removal to ensure better packing etc.

This however means the dfa dump is difficult (it is possible using
multiple dumpes) to match up to the chfa that the kernel is
using. Make this easier by making the dfa dump be able to take
the remapping as input, and provide an option to dump the
chfa equivalent hfa.

Renumbered states will show up as {new <== {orig}} in the dump

Eg.
```
--D dfa-states
{1} <== priority (allow/deny/prompt/audit/quiet)
{5} 0 (0x 4/0//0/0/0)

{1} perms: none
    0x2 -> {5}  0 (0x 4/0//0/0/0)
    0x4 -> {5}  0 (0x 4/0//0/0/0)
    \a 0x7 -> {5}  0 (0x 4/0//0/0/0)
    \t 0x9 -> {5}  0 (0x 4/0//0/0/0)
    \n 0xa -> {5}  0 (0x 4/0//0/0/0)
    \  0x20 -> {5}  0 (0x 4/0//0/0/0)
    4 0x34 -> {3}
{3} perms: none
    0x0 -> {6}
{6} perms: none
    1 0x31 -> {5}  0 (0x 4/0//0/0/0)
```

```
-D dfa-compressed-states
{1} <== priority (allow/deny/prompt/audit/quiet)
{2 == {5}} 0 (0x 4/0//0/0/0)

{1} perms: none
    0x2 -> {2 == {5}}  0 (0x 4/0//0/0/0)
    0x4 -> {2 == {5}}  0 (0x 4/0//0/0/0)
    \a 0x7 -> {2 == {5}}  0 (0x 4/0//0/0/0)
    \t 0x9 -> {2 == {5}}  0 (0x 4/0//0/0/0)
    \n 0xa -> {2 == {5}}  0 (0x 4/0//0/0/0)
    \  0x20 -> {2 == {5}}  0 (0x 4/0//0/0/0)
    4 0x34 -> {3}
{3} perms: none
    0x0 -> {4 == {6}}
{4 == {6}} perms: none
    1 0x31 -> {2 == {5}}  0 (0x 4/0//0/0/0)
```

Signed-off-by: John Johansen <john.johansen@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1474
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: John Johansen <john@jjmx.net>
2025-01-09 19:04:13 +00:00
John Johansen
50452e1147 parser: add a hfa dump that matches the renumbered chfa
Construction of the chfa can reorder states from what the numbering
given during the hfa constuctions because of reordering for better
compression, dead state removal to ensure better packing etc.

This however means the dfa dump is difficult (it is possible using
multiple dumpes) to match up to the chfa that the kernel is
using. Make this easier by making the dfa dump be able to take the
emapping as input, and provide an option to dump the chfa equivalent
hfa.

Renumbered states will show up as {new <== {orig}} in the dump

Eg.
--D dfa-states
{1} <== priority (allow/deny/prompt/audit/quiet)
{5} 0 (0x 4/0//0/0/0)

{1} perms: none
    0x2 -> {5}  0 (0x 4/0//0/0/0)
    0x4 -> {5}  0 (0x 4/0//0/0/0)
    \a 0x7 -> {5}  0 (0x 4/0//0/0/0)
    \t 0x9 -> {5}  0 (0x 4/0//0/0/0)
    \n 0xa -> {5}  0 (0x 4/0//0/0/0)
    \  0x20 -> {5}  0 (0x 4/0//0/0/0)
    4 0x34 -> {3}
{3} perms: none
    0x0 -> {6}
{6} perms: none
    1 0x31 -> {5}  0 (0x 4/0//0/0/0)

-D dfa-compressed-states
{1} <== priority (allow/deny/prompt/audit/quiet)
{2 == {5}} 0 (0x 4/0//0/0/0)

{1} perms: none
    0x2 -> {2 == {5}}  0 (0x 4/0//0/0/0)
    0x4 -> {2 == {5}}  0 (0x 4/0//0/0/0)
    \a 0x7 -> {2 == {5}}  0 (0x 4/0//0/0/0)
    \t 0x9 -> {2 == {5}}  0 (0x 4/0//0/0/0)
    \n 0xa -> {2 == {5}}  0 (0x 4/0//0/0/0)
    \  0x20 -> {2 == {5}}  0 (0x 4/0//0/0/0)
    4 0x34 -> {3}
{3} perms: none
    0x0 -> {4 == {6}}
{4 == {6}} perms: none
    1 0x31 -> {2 == {5}}  0 (0x 4/0//0/0/0)

Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-03 14:18:50 -08:00
John Johansen
4fb3dbc7b3 parser: improve unreachable state removal
Currently states are added to the reachable set when they are popped
from the workqueue. This however can result in states being
added to the work queue multiple times and reprocessed.

Eg. If state 2 has the transitions, and 9 is not in the reachable set
  a -> 9
  b -> 9
  c -> 9
  d -> 9
  e -> 3

then 9 will get pushed onto the work 4 times. Even worse other states
on the workqueue may also add state 9 to the workqueue because it has
not been added to the reachable set.

Instead add states to the reachable set when they are added to the
workqueue. The first encounter with a state will result in it being
reachable and all other encounters will see that it already in the set
and not add it to the workqueue.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-01 17:16:17 -08:00
John Johansen
45964d34e7 parser: add the abilitiy to dump the permissions table
Instead of encoding permissions in the accept and accept2 tables
extended perms uses a permissions table and accept becomes an index
into the table.

Add the ability to dump the permissions table so that it can be
compared and debugged.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-11-06 11:44:30 -08:00
John Johansen
2737cb2c2b parser: minimization - remove unnecessary second minimization pass
Moving apply_and_clear_deny() before the first minimization pass, which
was necessary to propperly support building accept information for
older none extended permission dfas, allows us to also get rid of doing a
second minimization pass if we want to force clearing explicit deny
info from extended permission tables.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-08-14 17:15:24 -07:00
John Johansen
1fa45b7c1f parser: dfa minimization prepare for extended permissions
Instead of compressing the permission set into 128 bit and using that
as the index in the permission map, just use the permissions directly
as the index into the permission map.

Note: this will break equality and minimization tests. Because deny
is not being cleared it will result in more partitions in the initial
setup. This will be addressed and the tests will be fixed in a follow
on patch.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-08-14 17:15:24 -07:00
John Johansen
5ff00bba3a parser: drop unused hash minimization remnants
Hash minimization was removed in
  f0b154528 Fix dfa minimization

however some remnants of minimization remained. A comment and the use
of the hash but only as a 0 value. Drop this dead code and comment.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-08-14 17:15:24 -07:00
John Johansen
b72cae79cb parser: support uin128_t key as a pair of uint64_t numbers
__uint128 is not supported by gcc on 32 bit architectures so rework
the 128 bit map key to be a pair of 64bit numbers.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-08-14 15:47:13 -07:00
John Johansen
373c095b3e parser: switch backend to perm32_t for permission bits
switch permission bits to use perm32_t type. This is just annotating
the code as it is no different than uint32_t at this time.

We do not convert the accept values as they may be mapped permission
bits or they may be and index value.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-08-14 15:47:13 -07:00
John Johansen
5bd2271189 pass prompt info down into the backend for mapping
mapping for PROMPT_DEV needs to know that we should prompt
2024-08-14 15:47:13 -07:00
John Johansen
2510698f63 parser: make minimization sets take prompt into account
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-08-14 15:47:13 -07:00
John Johansen
5c2bd20720 parser: pass rule mode prompt through to backend
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-08-14 15:47:13 -07:00
John Johansen
e29f5ce5f3 parser: if extended perms are supported by the kernel build a permstable
If extended permissions are supported use them. We need to build a
permission table and set the accept state of the chfa up as an index
into the table.

For now map the front end permission layout into the old format and
then convert that to the perms table just as the kernel does.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-08-14 14:39:18 -07:00
John Johansen
74b101faa8 Merge parser: improve dfa generation
speedup and reduce memory usage of dfa generation

A variety of changes to improve dfa generation
- By switching to Nodevec instead of Node sets we can reduce memory usage slightly and reduce code
- By using charsets for chars we reduce code and increase chances of node merging/reduction which reduces memory usage slightly
- By merging charsets we reduce the number of nodes

Signed-off-by: John Johansen <john.johansen@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1066
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
2023-07-11 02:26:10 +00:00
John Johansen
501e87a3f2 parser: Cleanup parser control flags, so they display as expected to user
Instead of having multiple tables, since we have room post split
of optimization and dump flags just move all the optimization and
dump flags into a common table.

We can if needed switch the flag entry size to a long in the future.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2023-07-08 19:58:59 -07:00
John Johansen
e84e481263 parser: cleanup and rework optimization and dump flag handling
In preparation for more flags (not all of the backend dfa based),
rework the optimization and dump flag handling which has been exclusively
around the dfa up to this point.

- split dfa control and dump flags into separate fields. This gives more
  room for new flags in the existing DFA set
- rename DFA_DUMP, and DFA_CONTROL to CONTROL_DFA and DUMP_DFA as
  this will provide more uniform naming for none dfa flags
- group dump and control flags into a structure so they can be passed
  together.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2023-07-07 17:47:41 -07:00
John Johansen
61c20a0ae8 convert anodes to using NodeVecs instead of NodeSets
Signed-off-by: John Johansen <john.johansen@canonical.com>
2023-07-03 00:53:26 -07:00
John Johansen
53d00b4d2b rename hashedNodeVec to NodeVec
Shorten the name length by dropping the leading "hashed".

Signed-off-by: John Johansen <john.johansen@canonical.com>
2023-07-03 00:49:56 -07:00
Alfonso Sánchez-Beato
5aab543a3b parser: replace dynamic_cast with is_type method
The dynamic_cast operator is slow as it needs to look at RTTI
information and even does some string comparisons, especially in deep
hierarchies like the one for Node. Profiling with callgrind showed
that dynamic_cast can eat a huge portion of the running time, as it
takes most of the time that is spent in the simplify_tree()
function. For some complex profiles, the number of calls to
dynamic_cast can be in the range of millions.

This commit replaces the use of dynamic_cast in the Node hierarchy
with a method called is_type(), which returns true if the pointer can
be casted to the specified type. It works by looking at a Node object
field that is an integer with bits set for each type up in the
hierarchy. Therefore, dynamic_cast is replaced by a simple bits
operation.

This change can reduce the compilation times for some profiles more
that 50%, especially in arm/arm64 arch. This opens the door to maybe
avoid "-O no-expr-simplify" in the snapd daemon, as now that option
would make the compilation slower in almost all cases.

This is the example profile used in some of my tests, with this change
the run-time is around 1/3 of what it was before on an x86 laptop:

profile "test" (attach_disconnected,mediate_deleted) {
dbus send
    bus={fcitx,session}
    path=/inputcontext_[0-9]*
    interface=org.fcitx.Fcitx.InputContext
    member="{Close,Destroy,Enable}IC"
    peer=(label=unconfined),
dbus send
    bus={fcitx,session}
    path=/inputcontext_[0-9]*
    interface=org.fcitx.Fcitx.InputContext
    member=Reset
    peer=(label=unconfined),
dbus receive
    bus=fcitx
    peer=(label=unconfined),
dbus receive
    bus=session
    interface=org.fcitx.Fcitx.*
    peer=(label=unconfined),
dbus send
    bus={fcitx,session}
    path=/inputcontext_[0-9]*
    interface=org.fcitx.Fcitx.InputContext
    member="Focus{In,Out}"
    peer=(label=unconfined),
dbus send
    bus={fcitx,session}
    path=/inputcontext_[0-9]*
    interface=org.fcitx.Fcitx.InputContext
    member="{CommitPreedit,Set*}"
    peer=(label=unconfined),
dbus send
    bus={fcitx,session}
    path=/inputcontext_[0-9]*
    interface=org.fcitx.Fcitx.InputContext
    member="{MouseEvent,ProcessKeyEvent}"
    peer=(label=unconfined),
dbus send
    bus={fcitx,session}
    path=/inputcontext_[0-9]*
    interface=org.freedesktop.DBus.Properties
    member=GetAll
    peer=(label=unconfined),
dbus (send)
    bus=session
    path=/org/a11y/bus
    interface=org.a11y.Bus
    member=GetAddress
    peer=(label=unconfined),
dbus (send)
    bus=session
    path=/org/a11y/bus
    interface=org.freedesktop.DBus.Properties
    member=Get{,All}
    peer=(label=unconfined),
dbus (receive, send)
    bus=accessibility
    path=/org/a11y/atspi/**
    peer=(label=unconfined),
dbus (send)
    bus=system
    path=/org/freedesktop/Accounts
    interface=org.freedesktop.DBus.Introspectable
    member=Introspect
    peer=(label=unconfined),
dbus (send)
    bus=system
    path=/org/freedesktop/Accounts
    interface=org.freedesktop.Accounts
    member=FindUserById
    peer=(label=unconfined),
dbus (receive, send)
    bus=system
    path=/org/freedesktop/Accounts/User[0-9]*
    interface=org.freedesktop.DBus.Properties
    member={Get,PropertiesChanged}
    peer=(label=unconfined),
dbus (send)
    bus=session
    interface=org.gtk.Actions
    member=Changed
    peer=(name=org.freedesktop.DBus, label=unconfined),
dbus (receive)
    bus=session
    interface=org.gtk.Actions
    member={Activate,DescribeAll,SetState}
    peer=(label=unconfined),
dbus (receive)
    bus=session
    interface=org.gtk.Menus
    member={Start,End}
    peer=(label=unconfined),
dbus (send)
    bus=session
    interface=org.gtk.Menus
    member=Changed
    peer=(name=org.freedesktop.DBus, label=unconfined),
dbus (send)
    bus=session
    path="/com/ubuntu/MenuRegistrar"
    interface="com.ubuntu.MenuRegistrar"
    member="{Register,Unregister}{App,Surface}Menu"
    peer=(label=unconfined),
}
2021-02-16 10:23:10 +01:00
Steve Beattie
461d9c2294
treewide: spelling/typo fixes in comments and docs
With the exception of the documentation fixes, these should all be
invisible to users.

Signed-off-by: Steve Beattie <steve.beattie@canonical.com>
Acked-by: Christian Boltz <apparmor@cboltz.de>
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/687
2020-12-01 12:47:11 -08:00
John Johansen
c9d01a325d parser: don't apply exec mapping computations to the policydb
v8 network permissions extend into the range used by exec mapping
so it is important to not blindly do execmapping on both the
file dfa and policydb dfa any more.

Track what type of dfa and its permissions we are building so
we can properly apply exec mapping only when building the
file dfa.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/521
Signed-off-by: John Johansen <john.johansen@canonical.com>
2020-09-29 03:34:47 -07:00
John Johansen
16b67ddbd6 add ability to use out of band transitions
Currently the NULL character is used as an out of band transition
for string/path elements. This works for them as the NULL character
is not valid for this data. However this does not work for binary
data that can contain a NULL character.

So far we have only dealt with fixed length fields of binary data
making the NULL separator either unnecessary.

However binary data like in the xattr match and mount data field are
variable length and can contain NULL characters. To deal with this
add the ability to specify out of band transitions, that can only
be triggered by code not input data.

The out of band transition can be used to separate variable length
data fields just as the NULL transition has been used to separate
variable length strings.

In the compressed hfa out of band transitions are expressed as a
negative offset from the states base. This leaves us room to expand
the character match range in the future if desired and on average
makes the range between the out of band transition and the input
transitions smaller than would be had if the out of band transition
had been stored after the valid input transitions.

Out of band transitions in the dfa will not break old kernels
that don't know about them, but they won't be able to trigger
the out of band transition match. So they should not be used unless
the kernel indicates that it supports them.

It should be noted that this patch only adds support for a single
out of band transition. If multiple out of band transitions are
required. It is trivial to extend.
- Add a tag indicating support in the kernel
- add a oob max range field to the dfa header so the kernel knows
  what the max range that needs verifying is.
- extend oob generation fns to generate oob based on value instead
  of a fixed -1.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2019-11-26 21:32:08 -08:00
John Johansen
7c29bfebe3 parser: improve debug output of transhar
Make transchar stream output work with a broader range of values.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2019-11-26 21:32:08 -08:00
John Johansen
72f93d9aba parser: rename uchar to transchar
Signed-off-by: John Johansen <john.johansen@canonical.com>
2019-11-26 21:32:08 -08:00
John Johansen
daa10d3ce1 parser: rework backend to allow for more transitions
As a step in preparing for out of band transitions and double walk
transitions rework the backend from using a char index to a class
with an larger range than char.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2019-11-26 21:32:08 -08:00
John Johansen
d22a867723 Fix compilation of audit modifiers
This fixes the incorrect compilation of audit modifiers for exec and
pivot_root as detailed in

https://launchpad.net/bugs/1431717
https://launchpad.net/bugs/1432045

The permission accumulation routine on the backend was incorrectly setting
the audit mask based off of the exec type bits (info about the exec) and
not the actual exec permission.

This bug could have also caused permissions issues around overlapping exec
generic and exact match exec rules, except the encoding of EXEC_MODIFIERS
ensured that the
  exact_match_allow & AA_USER/OTHER_EXEC_TYPE
  test would never fail for a permission accumulation with the exec permission
  set.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Steve Beattie <steve@nxnw.org>
2015-03-18 10:05:55 -07:00
John Johansen
fb53ec793b parser: Refactor add_new_state into two versions
Refactor add_new_state into two versions, one that splits anodes from
nnodes, and one for use when anodes and nnodes are presplit

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Steve Beattie <steve@nxnw.org>
Acked-by: Seth Arnold <seth.arnold@canonical.com>
2014-09-03 14:36:08 -07:00
John Johansen
df961a3e02 parser: Refactor the process_work_queue code into its own fn
Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Steve Beattie <steve@nxnw.org>
Acked-by: Seth Arnold <seth.arnold@canonical.com>
2014-09-03 14:32:52 -07:00
John Johansen
7ba571395e Fixes to that where dropped from the diff-encode patch
This diff is part of the diffencode patch but was dropped when it was
applied to bzr. I have no idea why and status showed a clean tree.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2014-01-09 17:24:40 -08:00
John Johansen
f0b154528d Fix dfa minimization
So DFA minimization has a bug and feature that keeps it from  minimizing
some dfas completely. This feature/bug did not result in incorrect dfas,
it just fails to result in full minimization.

The same mappings comparison is wrong. Or more correctly it is right when
transitions are not remapped to minimization partitions, but it may be
wrong when states are remapped. This means it will cause excess
partitioning (not removing all the states it should).

The trans hashing does a "guess" at partition splitting as a performance
enhancement. Basically it leverages the information that states that have
different transitions or transitions on different characters are not the
same. However this isn't always the case, because minimization can cause
some of those transitions to be altered. In previous testing this was
always a win, with only a few extra states being added some times. However
this changes with when the same mappings are fixed, as the hashing that was
done was based on the same flawed mapping as the broken same mappings.

If the same mappings are fixed and the hashing is not removed then there
is little to no change. However with both changes applied some dfas see
significant improvements. These improvements often result in performance
improvements despite minimization doing more work, because it means less
work to be done in the chfa comb compression

eg. test case that raised the issue (thanks tyler)
  /t { mount fstype=ext2, mount, }

  used to be minimized to
   {1} <== (allow/deny/audit/quiet)
   {6} (0x 2/0/0/0)

   {1} -> {2}: 0x7
   {2} -> {3}: 0x0
   {2} -> {2}: []
   {3} -> {4}: 0x0
   {3} -> {3}: []
   {4} -> {6}: 0x0
   {4} -> {7}: 0x65 e
   {4} -> {5}: []
   {5} -> {6}: 0x0
   {5} -> {5}: []
   {6}  (0x 2/0/0/0) -> {6}: [^\0x0]
   {7} -> {6}: 0x0
   {7} -> {8}: 0x78 x
   {7} -> {5}: []
   {8} -> {6}: 0x0
   {8} -> {5}: 0x74 t
   {8} -> {5}: []

  with the patch it is now properly minimized to
    {1} <== (allow/deny/audit/quiet)
    {6} (0x 2/0/0/0)

    {1} -> {2}: 0x7
    {2} -> {3}: 0x0
    {2} -> {2}: []
    {3} -> {4}: 0x0
    {3} -> {3}: []
    {4} -> {6}: 0x0
    {4} -> {4}: []
    {6}  (0x 2/0/0/0) -> {6}: [^\0x0]


The evince profile set sees some significant improvements picking a couple
example from its "minimized" dfas (it has 12) we see a reduction from 9720
states to 6232 states, and 6537 states to 3653 states. All told seeing the
performance/profile size going from
  2.8 parser: 4.607s 1007267 bytes
  dev head:   3.48s  1007267 bytes
  min fix:    2.68s  549603 bytes

of course evince is an extreme example so a few more

firefox
   2.066s   404549 bytes
 to
   1.336s   250585 bytes


cupsd
   0.365s   90834 bytes
 to
   0.293s   58855 bytes

dnsmasq
   0.118s   35689 bytes
 to
   0.112s   27992 bytes


smbd
   0.187s   40897 bytes
 to
   0.162s   33665 bytes


weather applet profile from ubuntu touch
   0.618s   105673 bytes
 to
   0.432s   89300 bytes


I have not seen a case where the parser regresses on performance but it is
possible. This patch will not cause a regression on generated policy size,
at worst it will result in policy that is the same size

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Tyler Hicks <tyhicks@canonical.com>
Acked-by: Steve Beattie <steve@nxnw.org>
2014-01-09 17:06:48 -08:00
John Johansen
22855508e8 Add Differential State Compression to the DFA
Differential state compression encodes a state's transitions as the
difference between the state and its default state (the state it is
relative too).

This reduces the number of transitions that need to be stored in the
transition table, hence reducing the size of the dfa.  There is a
trade off in that a single input character may have to traverse more
than one state.  This is somewhat offset by reduced table sizes providing
better locality and caching properties.

With carefully encoding we can still make constant match time guarentees.
This patch guarentees that a state that is differentially encoded will do at
most 3m state traversal to match an input of length m (as opposed to a
non-differentially compressed dfa doing exactly m state traversals).
In practice the actually number of extra traversals is less than this becaus
we selectively choose which states are differentially encoded.

In addition to reducing the size of the dfa by reducing the number of
transitions that have to be stored.  Differential encoding reduces the
number of transitions that need to be considered by comb compression,
which can result in tighter packing, due to a reduction in sparseness, and
also reduces the time spent in comb compression which currently uses an
O(n^2) algorithm.

Differential encoding will always result in a DFA that is smaller or equal
in size to the encoded DFA, and will usually improve compilation times,
with the performance improvements increasing as the DFA gets larger.

Eg. Given a example DFA that created 8991 states after minimization.
* If only comb compression (current default) is used

 52057 transitions are packed into a table of 69591 entries. Achieving an
 efficiency of about 75% (an average of about 7.74 table entries per state).
 With a resulting compressed dfa16 size of 404238 bytes and a run time for
 the dfa compilation of
   real 0m9.037s
   user 0m8.893s
   sys  0m0.036s

* If differential encoding + comb compression is used, 8292 of the 8991
  states are differentially encoded, with 31557 trans removed.  Resulting in

  20500 transitions are packed into a table of 20675 entries.  Acheiving an
  efficiency of about 99.2% (an average of about 2.3 table entries per state
  With a resulting compressed dfa16 size of 207874 bytes (about 48.6%
  reduction) and a run time for the dfa compilation of
   real 0m5.416s (about 40% faster)
   user 0m5.280s
   sys  0m0.040s

Repeating with a larger DFA that has 17033 states after minimization.
* If only comb compression (current default) is used

 102992 transitions are packed into a table of 137987 entries.  Achieving
 an efficiency of about 75% (an average of about 8.10 entries per state).
 With a resultant compressed dfa16 size of 790410 bytes and a run time for d
 compilation of
  real  0m28.153s
  user  0m27.634s
  sys   0m0.120s

* with differential encoding
 39374 transition are packed into a table of 39594 entries. Achieving an
 efficiency of about 99.4% (an average of about 2.32 entries per state).
 With a resultant compressed dfa16 size of 396838 bytes (about 50% reduction
 and a run time for dfa compilation of
  real  0m11.804s (about 58% faster)
  user  0m11.657s
  sys   0m0.084s

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Seth Arnold <seth.arnold@canonical.com>
2014-01-09 16:55:55 -08:00
John Johansen
41b454f2e5 Older C++ compilers complain about the use of a class with a non trivial
constructor in a union.  Change the ProtoState class to use an init fn
instead of a constructor.
2012-05-30 14:31:41 -07:00
John Johansen
f4240fcc74 Rename and invert logic of is_null to is_accept to better reflect its use
Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-03-22 13:21:55 -07:00
John Johansen
e7f6e0f9f1 Fix dfa minimization around the nonmatching state
The same mappings routine had two bugs in it, that in practice haven't
manifested because of partition ordering during minimization.  The
result is that some states may fail comparison and split, resulting
in them not being eliminated when they could be.

The first is that direct comparison to the nonmatching state should
not be done as it is a candiate for elimination, instead its partion
should be compared against.  This simplifies the first test


The other error is the comparison
  if (rep->otherwise != nonmatching)

again this is wrong because nomatching should not be directly
compared against.  And again can result in the current rep->otherwise
not being eliminated/replaced by the partion.  Again resulting in
extra trap states.

These tests where original done the way they were because
 ->otherwise could be null, which was used to represent nonmatching.
The code was cleaned up a while ago to remove this, ->otherwise is
always a valid pointer now.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-03-22 07:50:35 -07:00
John Johansen
7fcbd543d7 Factor all the permissions dump code into a single perms method
Also make sure the perms method properly switches to hex and back to dec
as some of the previous perm dump code did not.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-03-22 07:49:43 -07:00
John Johansen
5e361a4a05 Fix dfa minimization to deal with exec conflicts
Minimization was failing because it was too agressive.  It was minimizing
as if there was only 1 accept condition.  This allowed it to remove more
states but at the cost of loosing unique permission sets, they where
being combined into single commulative perms.  This means that audit,
deny, xtrans, ... info on one path would be applied to all other paths
that it was combined with during minimization.

This means that we need to retain the unique accept states, not allowing
them to be combined into a single state.  To do this we put each unique
permission set into its own partition at the start of minimization.

The states within a partition have the  same permissions and can be combined
within the other states in the partition as the loss of unique path
information is will not result in a conflict.

This is similar to what perm hashing used to do but deny information is
still being correctly applied and carried.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-03-09 04:20:19 -08:00
John Johansen
811d8aefa3 Fix transition character reporting of dfa dumps
Make them report a hex value strings instead of the default C++
\vvvvv

Make them consistent,
- Dump to report the default transition and what isn't transitioned
  on it.


Signed-off-by: John Johansen <john.johansen@canonical.com>
2012-03-09 04:18:35 -08:00
John Johansen
37f446dd79 Fix/cleanup the permission reporting for the dfa dumps
The permission reporting was not reporting the full set of permission
flags and was inconsistent between the dump routines.

Report permissions as the quad (allow/deny/audit/quiet) in hex.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-03-09 04:17:47 -08:00
John Johansen
1a01b5c296 Fix/cleanup the dfa dump routines output to provide state label
Fix the transitions states output so that they output the state label
instead of the state address.  That is
  {1} -> 0x10831a0:  /
now becomes
  {1} -> {2}:  /

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-03-09 04:14:34 -08:00
John Johansen
e61b7b9241 Update the copyright dates for the apparmor_parser
Signed-off-by: John Johansen <john.johansen@canonical.com>
2012-02-24 04:21:59 -08:00
John Johansen
6f95ff5637 Track full permission set through all stages of DFA construction.
Previously permission information was thrown away early and permissions
where packed to their CHFA form at the start of DFA construction.  Because
of this permissions hashing to setup the initial DFA partitions was
required as x transition conflicts, etc. could not be resolved.

Move the mapping of permissions to CHFA construction, and track the full
permission set through DFA construction.  This allows removal of the
perm_hashing hack, which prevented a full minimization from happening
in some DFAs.  It also could result in x conflicts not being correctly
detected, and deny rules not being fully applied in some situations.

Eg.
 pre full minimization
   Created dfa: states 33451
   Minimized dfa: final partitions 17033

 with full minimization
   Created dfa: states 33451
   Minimized dfa: final partitions 9550
   Dfa minimization no states removed: partitions 9550

The tracking of deny rules through to the completed DFA construction creates
a new class of states.  That is states that are marked as being accepting
(carry permission information) but infact are non-accepting as they
only carry deny information.  We add a second minimization pass where such
states have their permission information cleared and are thus moved into the
non-accepting partion.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Kees Cook <kees@ubuntu.com>
2012-02-16 07:41:40 -08:00
John Johansen
82a20d9bb8 Track deny and quiet perms during DFA construction
Delay the packing of audit and quiet permissions until chfa construction,
and track deny and quiet perms during DFA construction, so that we will
be able to do full minimization.  Also delay the packing of audit and

Signed-off-by: John Johansen <john.johansen@canonical.com>
2012-02-16 07:40:21 -08:00
John Johansen
f561b8cdfe Make hfa::match not need to walk a string twice
Currently hfa::match calls hfa::match_len to do matching.  However this
requires walking the input string twice.  Instead provide a match routine
for input that is supposed to terminate at a given input character.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-01-06 09:04:36 -08:00
John Johansen
3ff8b4d19a Add basic string matching to the hfa
Add the ability to match strings directly from the hfa instead of needing
to build a cfha.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-01-06 09:03:20 -08:00
John Johansen
18821b079b To reduce memory overhead of dfa creation convert to using a Node Vector
instead of a NodeSet.

We need to store sets of Nodes, to compute the dfa but the C++ set is
not the most efficient way to do this as, it has a has a lot of overhead
just to store a single pointer.

Instead we can use an array of tightly packed pointers + a some header
information.  We can do this because once the Set is finalized it will
not change, we just need to be able to reference and compare to it.

We don't use C++ Vectors as they have more overhead than a plain array
and we don't need their additional functionality.

We only replace the use of hashedNodeSets for non-accepting states as
these sets are only used in the dfa construction, and dominate the memory
usage.  The accepting states still may need to be modified during
minimization and there are only a small number of entries (20-30), so
it does not make sense to convert them.

Also introduce a NodeVec cache that serves the same purpose as the NodeSet
cache that was introduced earlier.

This is not abstracted this out as nicely as might be desired but avoiding
the use of a custom iterator and directly iterating on the Node array
allows for a small performance gain, on larger sets.

This patch reduces the amount of heap memory used by dfa creation by about
4x - overhead.  So for small dfas the savings is only 2-3x but on larger
dfas the savings become more and more pronounced.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Kees Cook <kees@ubuntu.com>
2011-12-15 05:16:03 -08:00
John Johansen
2674a8b708 Split the nodeset used in computing the dfa into two sets, accepting and
non-accepting, and have the proto-state use them.

To reduce memory overhead each set gains its own "cache" that make sure
there is only a single instance of each NodeSet generated.  And since
we have a cache abstraction, move relavent stats into it.

Also refactor code slightly to make caches and work_queue etc, DFA member
variables instead of passing them as parameters.

The split + caching results in a small reduction in memory use as the
cost of ProtoState + Caching is less than the redundancy that is eliminated.
However this results in a small decrease in performance.

Sorry I know this really should have been split into multiple patches
but the patch evolved and I got lazy and decided to just not bother
splitting it.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Kees Cook <kees@ubuntu.com>
2011-12-15 05:14:37 -08:00