[apparmor] [patch 4/5] parser: convert var expansion to use alternations
Seth Arnold
seth.arnold at canonical.com
Fri Dec 13 01:24:10 UTC 2013
On Mon, Dec 09, 2013 at 12:37:13PM -0800, Steve Beattie wrote:
> This patch converts the parser's variable expansion from adding new
> entries for each additional variable value to incorporating an
> alternation that includes all the values for the variable; e.g. given:
>
> @{BINS}=/bin /usr/bin /sbin /usr/sbin
> @{BINS}/binary ix,
>
> rather than expanding to exntries for
>
> /bin/binary
> /usr/bin/binary
> /sbin/binary
> /usr/sbin/binary
>
> one entry would remain that looks like:
>
> {/bin,/usr/bin,/sbin,/usr/sbin}/binary
>
> One complication with this patch is that we try to prevent mistakes for
> our users with variable expansion around '/'s; it's common for people to
> write profiles that contain things like:
>
> @{BAR}=/bingo/*/ /bango/
> /foo/@{BAR}/baz
>
> We already have a post-processing step that walks entries looking
> for multiple sequences of '/'s and filters them into single
> '/' which worked when creating new entries for each variable
> expansion. Converting to alternation expansion breaks this filtering,
> so code is added that removes leading and trailing slashes in variable
> values in the expansion if the character immediately preceding or
> following the variable is also a slash.
>
> The intent behind this is to reduce the amount of memory allocations
> and structure walking that needed to occur in when converting from the
> entry strings to the back end nodes. Examples with real world profiles
> showed performance improvements ranging from 2.5% to 10%. However,
> because the back end operations are sensitive to the front end inputs,
> it is possible for worse results to occur; for example, it takes the
> simple_tests/vars/vars_stress_0[123].sd tests significantly longer to
> complete after this patch is applied (vars_stress_03.sd in particular
> takes ~23 times longer). An initial analysis of profiling output in
> this negative case looks like it causes the tree simplification in
> the back end to do more work for unknown reasons.
>
> On the other hand, the test simple_tests/vars/vars_dbus_9.sd
> (introduced in "[patch 09/12] parser: more dbus variable testcases")
> takes ~1 sec to complete on my laptop before this patch, and roughly
> 0.01s with this patch applied.
>
> (One option would be to keep the "expand entries" approach as an
> alternative, but I couldn't come up with a good heuristic for when
> to use it instead.)
>
> Signed-off-by: Steve Beattie <steve at nxnw.org>
I think the leading and trailing slashes can probably be handled more
cleanly, and I think I'd like to see the free(*name) calls pulled up
towards the start of expand_by_alternations(), but there's nothing worth
holding up this patch.
Acked-by: Seth Arnold <seth.arnold at canonical.com>
Thanks
> ---
> parser/parser_variable.c | 113 +++++++++++++++++++++++++++++++++++------------
> 1 file changed, 85 insertions(+), 28 deletions(-)
>
> Index: b/parser/parser_variable.c
> ===================================================================
> --- a/parser/parser_variable.c
> +++ b/parser/parser_variable.c
> @@ -23,6 +23,8 @@
> #include <libintl.h>
> #include <linux/limits.h>
>
> +#include <string>
> +
> #define _(s) gettext(s)
>
> /* #define DEBUG */
> @@ -133,13 +135,90 @@ void free_var_string(struct var_string *
> free(var);
> }
>
> +static void trim_trailing_slash(std::string& str)
> +{
> + for (std::string::reverse_iterator rit = str.rbegin();
> + rit != str.rend() && *rit == '/'; ++rit) {
> + /* yuck, reverse_iterators are ugly */
> + str.erase(--rit.base());
> + }
> +}
> +
> +static void write_replacement(const char separator, const char* value,
> + std::string& replacement, bool filter_leading_slash,
> + bool filter_trailing_slash)
> +{
> + const char *p = value;
> +
> + replacement.append(1, separator);
> +
> + if (filter_leading_slash)
> + while (*p == '/')
> + p++;
> +
> + replacement.append(p);
> + if (filter_trailing_slash)
> + trim_trailing_slash(replacement);
> +}
> +
> +static int expand_by_alternations(struct set_value **valuelist,
> + struct var_string *split_var,
> + char **name)
> +{
> + char *value, *first_value;
> + std::string replacement;
> + bool filter_leading_slash = false;
> + bool filter_trailing_slash = false;
> +
> + first_value = get_next_set_value(valuelist);
> + if (!first_value) {
> + PERROR("ASSERT: set variable (%s) should always have at least one value assigned to it\n",
> + split_var->var);
> + exit(1);
> + }
> +
> + value = get_next_set_value(valuelist);
> + if (!value) {
> + /* only one entry for the variable, so just sub it in */
> + free(*name);
> + if (asprintf(name, "%s%s%s",
> + split_var->prefix ? split_var->prefix : "",
> + first_value,
> + split_var->suffix ? split_var->suffix : "") == -1)
> + return -1;
> + return 0;
> + }
> +
> + if (split_var->prefix && split_var->prefix[strlen(split_var->prefix) - 1] == '/')
> + filter_leading_slash = true;
> + if (split_var->suffix && *split_var->suffix == '/')
> + filter_trailing_slash = true;
> +
> + write_replacement('{', first_value, replacement, filter_leading_slash, filter_trailing_slash);
> + write_replacement(',', value, replacement, filter_leading_slash, filter_trailing_slash);
> +
> + while ((value = get_next_set_value(valuelist))) {
> + write_replacement(',', value, replacement, filter_leading_slash, filter_trailing_slash);
> + }
> +
> + free(*name);
> + if (asprintf(name, "%s%s}%s",
> + split_var->prefix ? split_var->prefix : "",
> + replacement.c_str(),
> + split_var->suffix ? split_var->suffix : "") == -1) {
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> /* doesn't handle variables in options atm */
> -static int expand_entry_variables(char **name, void *entry,
> +static int expand_entry_variables(char **name, void *entry,
> int (dup_and_chain)(void *))
> {
> struct set_value *valuelist;
> - char *value;
> struct var_string *split_var;
> + int ret;
>
> if (!entry) /* can happen when entry is optional */
> return 0;
> @@ -157,34 +236,12 @@ static int expand_entry_variables(char *
> exit(1);
> }
>
> - value = get_next_set_value(&valuelist);
> - if (!value) {
> - PERROR("ASSERT: set variable (%s) should always have at least one value assigned to them\n",
> - split_var->var);
> - exit(1);
> - }
> - free(*name);
> - if (asprintf(name, "%s%s%s",
> - split_var->prefix ? split_var->prefix : "",
> - value,
> - split_var->suffix ? split_var->suffix : "") == -1)
> - return -1;
> -
> - while ((value = get_next_set_value(&valuelist))) {
> - if (!dup_and_chain(entry)) {
> - PERROR("Memory allocation error while handling set variable %s\n",
> - split_var->var);
> - exit(1);
> - }
> -
> - free(*name);
> - if (asprintf(name, "%s%s%s",
> - split_var->prefix ? split_var->prefix : "", value,
> - split_var->suffix ? split_var->suffix : "") == -1)
> - return -1;
> - }
> + ret = expand_by_alternations(&valuelist, split_var, name);
>
> free_var_string(split_var);
> + if (ret != 0)
> + return -1;
> +
> }
> return 0;
> }
>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 490 bytes
Desc: Digital signature
URL: <https://lists.ubuntu.com/archives/apparmor/attachments/20131212/7f3ed570/attachment.pgp>
More information about the AppArmor
mailing list