[cfe-dev] Prevent RetainCountChecker from Analyzing Function Bodies when They Have Certain Annotate Attribute

Malhar Thakkar via cfe-dev cfe-dev at lists.llvm.org
Sun Jul 16 01:33:38 PDT 2017


On Sat, Jul 15, 2017 at 4:39 AM, Devin Coughlin <dcoughlin at apple.com> wrote:

>
> On Jul 14, 2017, at 12:31 PM, Malhar Thakkar <cs13b1031 at iith.ac.in> wrote:
>
> evalCall-ing a function based on annotations worked for the most part
> except for the following scenario.
>
> If the definition of the annotated function is *after *it is called
> somewhere in the code, the RetainCountChecker is unable to "see" the
> annotation on this function (I checked this by printing debug messages in
> evalCall to see if the RetainCountChecker is able to find the annotation).
>
> Consider the following examples.
>
> *// Definition of annotated function after it is called.*
>
> #define NULL 0typedef struct
> {
> 	int ref;
> } isl_basic_map;
> void free(void *);
> __isl_give isl_basic_map *foo(__isl_take isl_basic_map *bmap);
> isl_basic_map *isl_basic_map_free(__isl_take isl_basic_map *bmap);
>
> __isl_give isl_basic_map *bar(__isl_take isl_basic_map *bmap) {
> 	bmap = foo(bmap);
> 	return isl_basic_map_free(bmap); *// Leak warning for 'bmap' raised here.*
> }
>
> __attribute__((annotate("rc_ownership_trusted_implementation"))) isl_basic_map *isl_basic_map_free(__isl_take isl_basic_map *bmap)
> {
> 	if (!bmap)
> 		return NULL;
>
> 	if (--bmap->ref > 0)
> 		return NULL;
>
> 	free(bmap);
> 	return NULL;
> }
>
>
> *// Definition of annotated function before it is called.*
>
> #define NULL 0typedef struct
> {
> 	int ref;
> } isl_basic_map;
> void free(void *);
> __isl_give isl_basic_map *foo(__isl_take isl_basic_map *bmap);
> isl_basic_map *isl_basic_map_free(__isl_take isl_basic_map *bmap);
>
> __attribute__((annotate("rc_ownership_trusted_implementation"))) isl_basic_map *isl_basic_map_free(__isl_take isl_basic_map *bmap)
> {
> 	if (!bmap)
> 		return NULL;
>
> 	if (--bmap->ref > 0)
> 		return NULL;
>
> 	free(bmap);
> 	return NULL;
> }
>
> __isl_give isl_basic_map *bar(__isl_take isl_basic_map *bmap) {
> 	bmap = foo(bmap);
> 	return isl_basic_map_free(bmap); *// No leak warning for 'bmap' raised here.*
> }
>
> It would really help me if someone could point out why the
> RetainCountChecker behaves this way.
>
>
> I don’t know. One thing to note is that unlike most annotations, this one
> seems like it belongs on the definition of the function and not its
> interface, so you probably want to use getDefinition() to get the FuncDecl
> corresponding to the definition of the function body.
>

Yes, this works. I have uploaded a patch <https://reviews.llvm.org/D34937>
on Phabricator for the same.

>
> Also, I have a few queries directed to Dr. Alexandre and Dr. Sven.
> I applied these trusted annotations to obj_free(), obj_cow() and
> obj_copy() as they have a pattern (__isl.*free, __isl.*cow and
> __isl.*copy).
>
> Do, functions of the type obj_alloc_* have the same pattern if at all they
> do? Also, are there any other functions which require such annotations?
>
>
> It would be helpful if you explained what the obj_alloc_* functions do. Do
> they malloc memory directly and return as __isl_give? Do they manipulate
> reference counts directly?
>

Generally, obj_alloc_* functions initialize all the fields (including
initializing the reference counter to 1) of that particular isl object
(obj) and then return obj as __isl_give.
However, there are certain scenarios in which the objects passed to
obj_alloc_* functions are *reference counted objects* and are annotated
with __isl_take in which case leak warnings are raised.

Example

*// In the below example, the reference counts of expr1 and expr2 are
not decremented.*

*// Here, expr1 and expr2 are reference counted objects.*

*// Analysis starts from '*isl_ast_node_for_get_cond'

__attribute__((annotate("rc_ownership_trusted_implementation")))
__isl_give isl_ast_expr *isl_ast_expr_copy(__isl_keep isl_ast_expr
*expr)
{
	if (!expr)
		return NULL;

	expr->ref++;
	return expr;
}

__isl_give isl_ast_expr *isl_ast_expr_alloc_binary(enum isl_ast_op_type type,
	__isl_take isl_ast_expr *expr1, __isl_take isl_ast_expr *expr2)
{
	isl_ctx *ctx;
	isl_ast_expr *expr = NULL;

	if (!expr1 || !expr2)
		goto error;

	ctx = isl_ast_expr_get_ctx(expr1);
	expr = isl_ast_expr_alloc_op(ctx, type, 2);
	if (!expr)
		goto error;

	expr->u.op.args[0] = expr1;
	expr->u.op.args[1] = expr2; *// Leak warning raised for expr1*

	return expr; *// Leak warning raised for expr2*error:
	isl_ast_expr_free(expr1);
	isl_ast_expr_free(expr2);
	return NULL;
}

__isl_give isl_ast_expr *isl_ast_node_for_get_cond(
	__isl_keep isl_ast_node *node)
{
	if (!node)
		return NULL;
	if (node->type != isl_ast_node_for)
		isl_die(isl_ast_node_get_ctx(node), isl_error_invalid,
			"not a for node", return NULL);
	if (!node->u.f.degenerate)
		return isl_ast_expr_copy(node->u.f.cond);

	return *isl_ast_expr_alloc_binary*(isl_ast_op_le,
				*isl_ast_expr_copy*(node->u.f.iterator),
				*isl_ast_expr_copy*(node->u.f.init));
}


> I feel that I'll have to add annotations to obj_dup() as well because
> although adding an annotation to obj_cow() guarantees that the
> RetainCountChecker will not enter obj_cow's body when it is called, the
> analyzer might begin its analysis from obj_cow() which may lead it to call
> obj_dup() and result in leak warnings.
>
>
> What does obj_dup() do?
>

obj_dup() duplicates the isl object passed to it in the sense that it
copies the fiels of the passed isl object to a new object of the same type.
However, it does not change the reference count.

Generally, obj_cow() calls obj_dup(). The summary of their functionalities
is as follows.

obj_dup: copy the data, don't actually touch obj->ref
obj_cow: decrement reference count by 1 if it is > 1 and call obj_dup, or
do nothing to the reference count.

Consider the following snippet of code to understand it better.

__isl_give isl_basic_map *isl_basic_map_dup(__isl_keep isl_basic_map *bmap)
{
	struct isl_basic_map *dup;

	if (!bmap)
		return NULL;
	dup = isl_basic_map_alloc_space(isl_space_copy(bmap->dim),
			bmap->n_div, bmap->n_eq, bmap->n_ineq);
	if (!dup)
		return NULL;
	dup_constraints(dup, bmap);
	dup->flags = bmap->flags;
	dup->sample = isl_vec_copy(bmap->sample);
	return dup;

}

__attribute__((annotate("rc_ownership_trusted_implementation")))
__isl_give isl_basic_map *isl_basic_map_cow

(__isl_take isl_basic_map *bmap)
{
	if (!bmap)
		return NULL;

	if (bmap->ref > 1) {
		bmap->ref--;
		bmap = *isl_basic_map_dup*(bmap);
	}
	if (bmap) {
		ISL_F_CLR(bmap, ISL_BASIC_SET_FINAL);
		ISL_F_CLR(bmap, ISL_BASIC_MAP_REDUCED_COEFFICIENTS);
	}
	return bmap;

}


> Devin
>
>
Regards,
Malhar

ᐧ
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20170716/23696f7f/attachment.html>


More information about the cfe-dev mailing list