Hi Peff,

On Wed, 24 Oct 2018, Jeff King wrote:

> The upload_pack_config() callback uses an if/else chain
> like:
> 
>   if (!strcmp(var, "a"))
>      ...
>   else if (!strcmp(var, "b"))
>      ...
>   etc
> 
> This works as long as the conditions are mutually exclusive,
> but one of them is not. 20b20a22f8 (upload-pack: provide a
> hook for running pack-objects, 2016-05-18) added:
> 
>   else if (current_config_scope() != CONFIG_SCOPE_REPO) {
>      ... check some more options ...
>   }
> 
> That was fine in that commit, because it came at the end of
> the chain.  But later, 10ac85c785 (upload-pack: add object
> filtering for partial clone, 2017-12-08) did this:
> 
>   else if (current_config_scope() != CONFIG_SCOPE_REPO) {
>      ... check some more options ...
>   } else if (!strcmp("uploadpack.allowfilter", var))
>      ...
> 
> We'd always check the scope condition first, meaning we'd
> _only_ respect allowfilter when it's in the repo config. You
> can see this with:
> 
>   git -c uploadpack.allowfilter=true upload-pack . | head -1
> 
> which will not advertise the filter capability (but will
> after this patch). We never noticed because:
> 
>   - our tests always set it in the repo config
> 
>   - in protocol v2, we use a different code path that
>     actually calls repo_config_get_bool() separately, so
>     that _does_ work. Real-world people experimenting with
>     this may be using v2.
> 
> The more recent uploadpack.allowrefinwant option is in the
> same boat.
> 
> There are a few possible fixes:
> 
>   1. Bump the scope conditional back to the bottom of the
>      chain. But that just means somebody else is likely to
>      make the same mistake later.
> 
>   2. Make the conditional more like the others. I.e.:
> 
>        else if (!current_config_scope() != CONFIG_SCOPE_REPO &&
>                 !strcmp(var, "uploadpack.notallowedinrepo"))
> 
>      This works, but the idea of the original structure was
>      that we may grow multiple sensitive options like this.
> 
>   3. Pull it out of the chain entirely. The chain mostly
>      serves to avoid extra strcmp() calls after we've found
>      a match. But it's not worth caring about those. In the
>      worst case, when there isn't a match, we're already
>      hitting every strcmp (and this happens regularly for
>      stuff like "core.bare", etc).
> 
> This patch does (3).
> 
> Signed-off-by: Jeff King <p...@peff.net>
> ---
> Phew. That was a lot of explanation for a small patch, but
> this was sufficiently subtle I thought it worth it. And
> also, I was really surprised the bug managed to exist for
> this long without anybody noticing.

Maybe a lot of explanation, but definitely a good one. The explanation and
the patch look good to me.

Thanks,
Dscho

> 
>  upload-pack.c | 9 ++++++---
>  1 file changed, 6 insertions(+), 3 deletions(-)
> 
> diff --git a/upload-pack.c b/upload-pack.c
> index 540778d1dd..489c18e222 100644
> --- a/upload-pack.c
> +++ b/upload-pack.c
> @@ -1028,14 +1028,17 @@ static int upload_pack_config(const char *var, const 
> char *value, void *unused)
>               keepalive = git_config_int(var, value);
>               if (!keepalive)
>                       keepalive = -1;
> -     } else if (current_config_scope() != CONFIG_SCOPE_REPO) {
> -             if (!strcmp("uploadpack.packobjectshook", var))
> -                     return git_config_string(&pack_objects_hook, var, 
> value);
>       } else if (!strcmp("uploadpack.allowfilter", var)) {
>               allow_filter = git_config_bool(var, value);
>       } else if (!strcmp("uploadpack.allowrefinwant", var)) {
>               allow_ref_in_want = git_config_bool(var, value);
>       }
> +
> +     if (current_config_scope() != CONFIG_SCOPE_REPO) {
> +             if (!strcmp("uploadpack.packobjectshook", var))
> +                     return git_config_string(&pack_objects_hook, var, 
> value);
> +     }
> +
>       return parse_hide_refs_config(var, value, "uploadpack");
>  }
>  
> -- 
> 2.19.1.1094.gd480080bf6
> 

Reply via email to