BUG/MEDIUM: map/acl: pat_ref_{set,delete}_by_id regressions

Some regressions were introduced by 5fea59754b ("MEDIUM: map/acl:
Accelerate several functions using pat_ref_elt struct ->head list")

pat_ref_delete_by_id() fails to properly unlink and free the removed
reference because it bypasses the pat_ref_delete_by_ptr() made for
that purpose. This function is normally used everywhere the target
reference is set for removal, such as the pat_ref_delete() function
that matches pattern against a string. The call was probably skipped
by accident during the rewrite of the function.

With the above commit also comes another undesirable change:
both pat_ref_delete_by_id() and pat_ref_set_by_id() directly use the
<refelt> argument as a valid pointer (they do dereference it).

This is wrong, because <refelt> is unsafe and should be handled as an
ID, not a pointer (hence the function name). Indeed, the calling function
may directly pass user input from the CLI as <refelt> argument, so we must
first ensure that it points to a valid element before using it, else it is
probably invalid and we shouldn't touch it.

What this patch essentially does, is that it reverts pat_ref_set_by_id()
and pat_ref_delete_by_id() to pre 5fea59754b behavior. This seems like
it was the only optimization from the patch that doesn't apply.

Hopefully, after reviewing the changes with Fred, it seems that the 2
functions are only being involved in commands for manipulating maps or
acls on the cli, so the "missed" opportunity to improve their performance
shouldn't matter much. Nonetheless, if we wanted to speed up the reference
lookup by ID, we could consider adding an eb64 tree for that specific
purpose that contains all pattern references IDs (ie: pointers) so that
eb lookup functions may be used instead of linear list search.

The issue was raised by Marko Juraga as he failed to perform an an acl
removal by reference on the CLI on 2.9 which was known to work properly
on other versions.

It should be backported on 2.9.

Co-Authored-by: Frédéric Lécaille <flecaille@haproxy.com>
This commit is contained in:
Aurelien DARRAGON 2023-12-08 11:46:15 +01:00 committed by Christopher Faulet
parent 1a19e4e7af
commit d7964c52ce

View File

@ -1606,17 +1606,25 @@ void pat_ref_delete_by_ptr(struct pat_ref *ref, struct pat_ref_elt *elt)
free(elt);
}
/* This function removes all the patterns matching the pointer <refelt> from
/* This function removes the pattern matching the pointer <refelt> from
* the reference and from each expr member of this reference. This function
* returns 1 if the entry was found and deleted, otherwise zero.
*
* <refelt> is user input: it is provided as an ID and should never be
* dereferenced without making sure that it is valid.
*/
int pat_ref_delete_by_id(struct pat_ref *ref, struct pat_ref_elt *refelt)
{
int ret = !!refelt->node.node.leaf_p;
struct pat_ref_elt *elt, *safe;
ebmb_delete(&refelt->node);
return ret;
/* delete pattern from reference */
list_for_each_entry_safe(elt, safe, &ref->head, list) {
if (elt == refelt) {
pat_ref_delete_by_ptr(ref, elt);
return 1;
}
}
return 0;
}
/* This function removes all patterns matching <key> from the reference
@ -1739,13 +1747,21 @@ static inline int pat_ref_set_elt(struct pat_ref *ref, struct pat_ref_elt *elt,
/* This function modifies the sample of pat_ref_elt <refelt> in all expressions
* found under <ref> to become <value>, after checking that <refelt> really
* belongs to <ref>.
*
* <refelt> is user input: it is provided as an ID and should never be
* dereferenced without making sure that it is valid.
*/
int pat_ref_set_by_id(struct pat_ref *ref, struct pat_ref_elt *refelt, const char *value, char **err)
{
if (refelt->node.node.leaf_p) {
if (!pat_ref_set_elt(ref, refelt, value, err))
return 0;
return 1;
struct pat_ref_elt *elt;
/* Look for pattern in the reference. */
list_for_each_entry(elt, &ref->head, list) {
if (elt == refelt) {
if (!pat_ref_set_elt(ref, elt, value, err))
return 0;
return 1;
}
}
memprintf(err, "key or pattern not found");