Re: Leak Sanitizer - how to suppress leaks

2019-09-23 Thread David Holland
On Mon, Sep 16, 2019 at 07:47:24AM +0700, Robert Elz wrote:
 >   | There have been OSes in the past where memory not freed yet at process
 >   | exit is _not_ freed by the system, and there might be again,
 > 
 > Please everyone, let's retain some perspective.   Systems like those
 > (Roy mentioned RTEMS as an example) require specially constructed code,
 > as in a system where process termination doesn't free all the process's
 > resources, then what

The OS I was thinking of was a desktop OS that could (and did) run
quite a bit of unix code. As I recall the various C runtimes available
took some steps to avoid gaping memory leaks, but there's still no
reason to not tidy up when one can.

 > The one reason for doing this kind of free() is so that LSan type analysers
 > can look at memory and report anything that wasn't freed.

This is, however, itself a pretty good reason.

-- 
David A. Holland
dholl...@netbsd.org


Re: Leak Sanitizer - how to suppress leaks

2019-09-17 Thread John Nemeth
On Sep 16,  3:02am, Kamil Rytarowski wrote:
} On 16.09.2019 02:47, Robert Elz wrote:
} > Expecting any unix program (even the simplest) to simply compile and run
} > in a non-kernel environment is pointless, they simply won't work.
} 
} As a non-trivial no-kernel example, nethack on rump unikernel:
} 
} https://github.com/anttikantee/rumprun-nethack

 This is certainly an interesting case.  However, in this case,
the "kernel", becomes part of the application.  As soon as the
application exits, the kernel exits as well, so all memory ceases
to exist.

}-- End of excerpt from Kamil Rytarowski


Re: Leak Sanitizer - how to suppress leaks

2019-09-17 Thread John Nemeth
On Sep 16,  7:47am, Robert Elz wrote:
} 
} I used to program such things in the past (distant past) - one of the
} requirements of the particular system I was using was that processes
} were not allowed to run for "too long" before calling the system process
} switch function (no kernel running clock interrupts to do time slicing).

 That was Windows for a very long time, i.e. co-operative
multi-tasking.  I wrote one Windows app in the Windows 3.1/95 era.
I hated it.  Terrible programming environment.  Heck, all of Windows
would stop running when you ran the application under a debugger.
At least I figured out a dual monitor setup (Windows running on a
VGA adapter and the debugger running on a Hercules adapter).

}   | In cases where it _is_ expensive, or at least where it's expensive to
}   | figure out, the same argument applies as against garbage collection:
}   | if you aren't sure what the lifetime of that object is, and the
}   | program isn't structured in a way that allows being reasomably sure it
}   | is disposed of exactly once, how can you have confidence in any other
}   | correctness properties?
} 
} That isn't the issue at all - in the programs in question, there's no
} issue with the lifetimes of objects, it is from creation until something
} explicitly makes them go away, or process exit, whichever comes first.

 Process exit does it much more efficiently as well.  Instead
of trying to find every object and free memory a few bytes at a
time, the entire address space is freed in fell swoop.

}-- End of excerpt from Robert Elz


Re: Leak Sanitizer - how to suppress leaks

2019-09-15 Thread Kamil Rytarowski
On 16.09.2019 02:47, Robert Elz wrote:
> Expecting any unix program (even the simplest) to simply compile and run
> in a non-kernel environment is pointless, they simply won't work.

As a non-trivial no-kernel example, nethack on rump unikernel:

https://github.com/anttikantee/rumprun-nethack

On 16.09.2019 02:47, Robert Elz wrote:> Also remember, that it is not
only exit (via normal means, via an error
> exit, or even killed by a signal) that requires cleanup, but exec() as
> well.

LSan handles forks with/without exec. If there was a fork(2), without
execing an image, every leaked memory is reported.

On 16.09.2019 02:47, Robert Elz wrote:> doesn't apply, and processes
need to clean up after themselves
> evern when they abort.

Once someone picks the code and tries to make it a long-running process,
it's a matter of reworking abnormal termination code paths into fallbacks.

Everything depends on per-case basis.

Once valgrind/LSan or similar tool will catch more leaks in ps(1), I
will mark them with __NO_LEAKS as I find it more valuable to invest into
tools rather than manual work of squashing leaks.



signature.asc
Description: OpenPGP digital signature


Re: Leak Sanitizer - how to suppress leaks

2019-09-15 Thread Robert Elz
Date:Sun, 15 Sep 2019 19:42:06 +
From:David Holland 
Message-ID:  <20190915194206.gb6...@netbsd.org>

  | There have been OSes in the past where memory not freed yet at process
  | exit is _not_ freed by the system, and there might be again,

Please everyone, let's retain some perspective.   Systems like those
(Roy mentioned RTEMS as an example) require specially constructed code,
as in a system where process termination doesn't free all the process's
resources, then what

Kamil Rytarowski  said:
  | err(3) / abort(3) etc, are abnormal exit. Abnormal one differs from normal/ 
  | clean. For the same reason nobody registers a SIGFPU signal handler to free
  | resources.

doesn't apply, and processes need to clean up after themselves
evern when they abort.

If the system (whatever it is) can handle aborted processes without
suffering, but cannot handle cleanly exiting ones (which would be
bizarre, but never mind) then the simple solution for any process is
simply to always abort:
err(0, "normal exit");

Also remember, that it is not only exit (via normal means, via an error
exit, or even killed by a signal) that requires cleanup, but exec() as
well.

That means, for example, that if you truly believe that a program should
free all of its resources before exiting, then you must also believe that
it must do the same before exec() (as the process is gone after that
happens, and nothing else knows what memory it has or where it was put)
which makes implementations of routines like popen() and system() kind
of interesting to imagine.

This is why the unix environment defines it as a system responsibiliy to
do that cleanup, not the process' and that's the model in which we program.

Expecting any unix program (even the simplest) to simply compile and run
in a non-kernel environment is pointless, they simply won't work.

I used to program such things in the past (distant past) - one of the
requirements of the particular system I was using was that processes
were not allowed to run for "too long" before calling the system process
switch function (no kernel running clock interrupts to do time slicing).

Do we want to make all of our processes able to run in an environment
like that as well?

  | In cases where it _is_ expensive, or at least where it's expensive to
  | figure out, the same argument applies as against garbage collection:
  | if you aren't sure what the lifetime of that object is, and the
  | program isn't structured in a way that allows being reasomably sure it
  | is disposed of exactly once, how can you have confidence in any other
  | correctness properties?

That isn't the issue at all - in the programs in question, there's no
issue with the lifetimes of objects, it is from creation until something
explicitly makes them go away, or process exit, whichever comes first.

Unlike garbage collection systems, there's no difficulty in finding the
allocated objects, the issue is that there are *lots* of them, in lots
of different data structures, all of which in the "free before exit" model
have to be freed - lots of data structure walking, and not just once, as
these programs fork() a lot, and those child processes inherit all the
allocated memory - it would have to be freed in every one of the children.

And all for no useful purpose, as the system does the free for us when the
process image is deleted.   And we rely upon that everywhere.

The one reason for doing this kind of free() is so that LSan type analysers
can look at memory and report anything that wasn't freed.

kre



Re: Leak Sanitizer - how to suppress leaks

2019-09-15 Thread Roy Marples

On 15/09/2019 21:02, Marc Balmer wrote:

Doesn‘t exit clear all resources on posix systems?


Don't assume that exit would ever be called.
Code used in some RTOS, such as RTEMS, everything runs as a thread and 
thus cannot call exit.
In this case, at the natural programs exit, resources need to be cleaned 
up manually, as exit is the equivalent of shutdown.


Roy


Re: Leak Sanitizer - how to suppress leaks

2019-09-15 Thread Marc Balmer
Doesn‘t exit clear all resources on posix systems?

> Am 15.09.2019 um 21:42 schrieb David Holland :
> 
>> On Fri, Sep 13, 2019 at 07:03:40PM +0700, Robert Elz wrote:
>> It isn't so much that I think we need to save the cost of doing
>> the free() (though for ps it turns out to be harder than you'd expect
>> to actually get it right) but whether it is worth anyone time and
>> effort to actually work out what is needed (if anything at all).  Since
>> ps simply exits, we know there is no real leak, only the illusion of
>> one very briefly.
> 
> Keep in mind, though, that one of the roles of NetBSD has always been
> to serve as a reference implementation.
> 
> There have been OSes in the past where memory not freed yet at process
> exit is _not_ freed by the system, and there might be again, and in
> cases where it isn't expensive to do so it seems that we may as well
> tidy up properly so that the code will run acceptably on such OSes.
> 
> In cases where it _is_ expensive, or at least where it's expensive to
> figure out, the same argument applies as against garbage collection:
> if you aren't sure what the lifetime of that object is, and the
> program isn't structured in a way that allows being reasomably sure it
> is disposed of exactly once, how can you have confidence in any other
> correctness properties?
> 
> -- 
> David A. Holland
> dholl...@netbsd.org


Re: Leak Sanitizer - how to suppress leaks

2019-09-15 Thread David Holland
On Fri, Sep 13, 2019 at 07:03:40PM +0700, Robert Elz wrote:
 > It isn't so much that I think we need to save the cost of doing
 > the free() (though for ps it turns out to be harder than you'd expect
 > to actually get it right) but whether it is worth anyone time and
 > effort to actually work out what is needed (if anything at all).  Since
 > ps simply exits, we know there is no real leak, only the illusion of
 > one very briefly.

Keep in mind, though, that one of the roles of NetBSD has always been
to serve as a reference implementation.

There have been OSes in the past where memory not freed yet at process
exit is _not_ freed by the system, and there might be again, and in
cases where it isn't expensive to do so it seems that we may as well
tidy up properly so that the code will run acceptably on such OSes.

In cases where it _is_ expensive, or at least where it's expensive to
figure out, the same argument applies as against garbage collection:
if you aren't sure what the lifetime of that object is, and the
program isn't structured in a way that allows being reasomably sure it
is disposed of exactly once, how can you have confidence in any other
correctness properties?

-- 
David A. Holland
dholl...@netbsd.org


Re: Leak Sanitizer - how to suppress leaks,Re: Leak Sanitizer - how to suppress leaks

2019-09-14 Thread aran
From: Kamil Rytarowski 
Subject: Re: Leak Sanitizer - how to suppress leaks,Re: Leak Sanitizer - how to 
suppress leaks
Date: Sat, 14 Sep 2019 13:45:08 +0200

> On 13.09.2019 14:03, Robert Elz wrote:
>
>>   | I think we need to specify the definition. Leak is a memory object
>>   | without a pointer referencing it.
>>
>> I can accept that as a definition.   But we also need to recognise that
>> there are no leaks after the program has finished.   And the program has
>> finished as soon as any of exit()/exec*()/_exit() is called (and succeeds)
>> or when main() returns.   After that there's nothing left of interest.
>> Leak detection needs to happen before one of those events occurs.

May I propose an alternated definition?  An unused memory object is garbage and
there re two flavors of garbage: semantic and static.

Static garbage is memory the program can no longer reference.  This is what we
usually think of as leaked memory and this is the garbage that mark-and-sweep
style garbage collectors clean up.  Semantic garbage is memory that is simply no
longer used by the program, but could be reached.

In the following example, the byte with 'a' is static garbage while the byte
with 'b' is semantic garbage at the return statement:

  int main () {
char *p;
p  = malloc(1);
*p = 'a';
p  = malloc(1);
*p = 'b';
return 0;
  }

A program leaks memory if it creates garbage of either flavor without bounds.

>From a computability perspective, the halting problem reduces to identifying
semantic garbage.  It also reduces to identifying static garbage in languages
where we can do math with addresses, like C.

This doesn't mean that we shouldn't try to find and fix memory leaks.  It means
that there can't be fool proof method to do it in all cases.

Aran






pgpdayV74ycf0.pgp
Description: PGP signature


Re: Leak Sanitizer - how to suppress leaks

2019-09-14 Thread Alexander Nasonov
Martin Husemann wrote:
> On Sat, Sep 14, 2019 at 01:45:08PM +0200, Kamil Rytarowski wrote:

> > Thanks! I will go for __NO_LEAKS ifdef.
> 
> But it is not a good idea to clutter perfectly fine sources with such
> #ifdefs and unused/untested/likely broken code (even if it does not affect
> the default binaries).

I support __NO_LEAKS idea because it documents the fact that there
are indended leaks.

-- 
Alex


Re: Leak Sanitizer - how to suppress leaks

2019-09-14 Thread Kamil Rytarowski
On 14.09.2019 13:54, Martin Husemann wrote:
> On Sat, Sep 14, 2019 at 01:45:08PM +0200, Kamil Rytarowski wrote:
>> OK, thank you. So to sum it up. Plugging resource leaks costs time and
>> human effort and this is what is not wanted by some people. For those
>> who find it useless are not forced to do it, so can ignore it. For
>> others it can be useful activity.
>>
>> Thanks! I will go for __NO_LEAKS ifdef.
> 
> But it is not a good idea to clutter perfectly fine sources with such
> #ifdefs and unused/untested/likely broken code (even if it does not affect
> the default binaries).
> 
> Martin
> 
> 

This is right. I don't intend to do this now/soon. I don't intend to do
it before passing all the tests in LSan/ASan and before getting valgrind
aboard.

The most interesting target here is rumpkernel.



signature.asc
Description: OpenPGP digital signature


Re: Leak Sanitizer - how to suppress leaks

2019-09-14 Thread Martin Husemann
On Sat, Sep 14, 2019 at 01:45:08PM +0200, Kamil Rytarowski wrote:
> OK, thank you. So to sum it up. Plugging resource leaks costs time and
> human effort and this is what is not wanted by some people. For those
> who find it useless are not forced to do it, so can ignore it. For
> others it can be useful activity.
> 
> Thanks! I will go for __NO_LEAKS ifdef.

But it is not a good idea to clutter perfectly fine sources with such
#ifdefs and unused/untested/likely broken code (even if it does not affect
the default binaries).

Martin




Re: Leak Sanitizer - how to suppress leaks

2019-09-14 Thread Kamil Rytarowski
On 13.09.2019 14:03, Robert Elz wrote:

>   | I think we need to specify the definition. Leak is a memory object
>   | without a pointer referencing it.
> 
> I can accept that as a definition.   But we also need to recognise that
> there are no leaks after the program has finished.   And the program has
> finished as soon as any of exit()/exec*()/_exit() is called (and succeeds)
> or when main() returns.   After that there's nothing left of interest.
> Leak detection needs to happen before one of those events occurs.

No. There are destructors, there are atexit and atexit-like (per-thread)
callbacks.

We still can register new atexit entry from executing atexit handler. We
can legally allocate resources in atexit memory and free it in another
atexit.

It's similar with destructors.

It's not something that happens frequently, but it shows that main() is
not special. On the other hand fork(2)/exit(3)/exec(3) are special cases.

> 
>   | A special type of a leak is referencing memory that is no longer needed
>   | and could be freed.
> 
> That one will be interesting to find...
> 

Probably Coverity or a similar tool.

> 
>   | From the compiler point of view it is only slightly different than any
>   | regular "int foo(int, char**)".
> 
> From the compiler's viewpoint you're right, main() is just a function.
> But from the overall system's viewpoint it isn't.
> 

Sanitizers are a compiler feature, not looking at it from system's
viewpoint.

>   | From sanitizer point of view it is not distinguishable and we cannot
>   | (easily) make any conditions based on this.
> 
> This is a problem, as return from main, and a call to exit() are
> equivalent.   Perhaps we should stop returning from main, and always
> explicitly call exit() ?
> 

As already noted, main() is not special neither interesting.

>   | Actually globals are not used in LSan and can be ignored.
> 
> I don't know how to interpret that.   In many programs the pointers
> to allocated memory are stored in (or found from other data stored in)
> global vars.
> 
>   | Dynamic linker execution time is a part of execution time of the whole
>   | application.
> 
> Of course.
> 
>   | This implementation of __suppress_leak() takes 9-10 amd64 CPU
>   | instructions for each __suppress_leak() call.
> 
> What does each of those calls accomplish?   That is, how many of
> them are needed?
> 

It was proposed to mark a resource as legitimate leak.

>   | Before getting measurable performance impact of __suppress_leak() we
>   | will can go out of memory..
> 
> It isn't the call itself which is probably the issue, but traversing
> all the data structs (some of which may not have been used in ages, and
> might be paged out) in order to find all the pointers to the memory that
> is allocated.   Alternatively, we could do:
> 
>   void *
>   xmalloc(size_t n)
>   {
>   void *p = malloc(n);
> 
>   if (p == NULL) {
>   err(1, ""); /* or whatever is appropriate *.
>   }
> 
>   __suppress_leak(p);
> 
>   return p;
>   }
> 
> but is that going to help anything at all, ever?
> 

I was thinking about a variation of malloc(3) that can leak.
Unfortunately there are multiple types of allocators and certain
functions allocate resources internally, inside libraries.


>   | LSan does not look look for unclosed file descriptors so we can skip
>   | discussing the file descriptor leaks as offtopic here.
> 
> We can - but the issue is essentially the same.
> 

Valgrind could detect file descriptor leak. We can reschedule it for later.

Coverity should be able to perform the same task.

A problem here is that we inherit file descriptors from parent.
Detecting it and adding missing close-on-exec is however a good idea for
optimization in future.

LSan does not detect it as of today so it

>   | > If not, your program, according to you, is leaking file descriptors.
>   | No.
> 
> What is the difference?
> 

It is hypercorrectness, same like checking errno from printf(3) and
attempt to change the process of plugging resource leaks to absurd.

>   | Actually whenever I open a file descriptor I try to close it when no
>   | longer needed.
> 
> That's fine, and if you were to do, in some function
> 
>   func()
>   {
>   char buf[BIG_ENOUGH];
>   int fd;
> 
>   fd = open("myfile", 0);
>   if (fd < 0) /* whatever */;
>   read(fd, buf, BIG_ENOUGH);
> 
>   return buf[0] == '#';
>   }
> 
> I think we'd all agree that there's a fd leak in there.   Just the
> same as if fd was a pointer, and the open() was malloc() (and some other
> code replaced the read) it would be a memory leak.
> 

err(3) / abort(3) etc, are abnormal exit. Abnormal one differs from
normal/clean. For the same reason nobody registers a SIGFPU signal
handler to free resources.

The philosophy differs in fault tolerant environments, but it woul

Re: Leak Sanitizer - how to suppress leaks

2019-09-13 Thread Steffen Nurpmeso
Warner Losh wrote in :
 |On Thu, Sep 12, 2019, 7:24 PM Simon Burge <[1]sim...@netbsd.org[/1]> wrote:
 |
 |  [1] mailto:sim...@netbsd.org
 |
 ||Kamil Rytarowski wrote:
 |
 ||> I will revert it, but I am looking for a more generic approach.
 ||>
 ||> How about adding ifdef __NO_LEAKS like:
 ||>
 ||> #ifdef __NO_LEAKS
 ||> free(3)?
 ||> #endif
 ||>
 ||> And in lsan/asan/valgrind/etc checks use -D__NO_LEAKS.
 |
 ||Sorry if I'm missing something that has been already explained,
 ||but why (practically) do we care about memory leaks for a utility
 ||that is about to finish?
 |
 ||If we're doing some ugly #ifdef dance only when running the
 ||sanitiser(s), then we haven't actually done anything to "fix"
 ||the leak in the installed binaries so it seems that there was
 ||no practical problem that we were trying to solve in the first
 ||place.
 |
 |When multiple people are doing leak busting, maybe over years, they \
 |eliminate many false positives so you can focus on the real issues \
 |w/o a run time penalty. Especially something in the library that 
 |comes up often... otherwise they get in the way of making progress...

Also overall i found it results in better design.  I had atexit
and atexit_final and with debug enabled _start could call
(in-library) main several times in a row without causing leaks or
any other problems, since anything would have been correctly
destructed.  (Not using constructor or destructor nor any other
ELF attributes.)  It is much nicer to see "Memory cache is
entirely empty.", than not.  Imho.

--steffen
|
|Der Kragenbaer,The moon bear,
|der holt sich munter   he cheerfully and one by one
|einen nach dem anderen runter  wa.ks himself off
|(By Robert Gernhardt)


Re: Leak Sanitizer - how to suppress leaks

2019-09-13 Thread Robert Elz
Date:Fri, 13 Sep 2019 19:03:40 +0700
From:Robert Elz 
Message-ID:  <24855.1568376...@jinx.noi.kre.to>

  | Even lint had that ability from is beginning (30 years ago).

40.   I cannot subtract...  (and s/is/its/  - I cannot type either!)

kre




Re: Leak Sanitizer - how to suppress leaks

2019-09-13 Thread Robert Elz
Date:Fri, 13 Sep 2019 02:07:43 +0200
From:Kamil Rytarowski 
Message-ID:  <1ea33d0c-fb0f-ecdd-1706-b11841dc6...@gmx.com>

  | Please note that in all arguments about leak sanitization, that it uses
  | internally the same mechanism as garbage collectors. lsan and libgc (and
  | others) also server similar purpose.

Noted.

But please note that all of that means nothing to me at all.   Nor do I
care how any of those things work.   What they do is of some interest,
how they do it is not.

  | LSan can detect indirect leaks:

That's nice.

  | No more leaks were reported, if there are any I will address them.

See my (was very recent when I typed this...) reply to wiz's message.

  | LSan/NetBSD still needs more work and it is not expected to be perfect.

I am not asking for it to be perfect.   What I am requesting is intelligent
interpretation of its results, not just "LSan says this is wrong, so I must
immediately fix it".

  | How about adding ifdef __NO_LEAKS like:
  |
  | #ifdef __NO_LEAKS
  | free(3)?
  | #endif
  |
  | And in lsan/asan/valgrind/etc checks use -D__NO_LEAKS.

It isn't so much that I think we need to save the cost of doing
the free() (though for ps it turns out to be harder than you'd expect
to actually get it right) but whether it is worth anyone time and
effort to actually work out what is needed (if anything at all).  Since
ps simply exits, we know there is no real leak, only the illusion of
one very briefly.

The problem more is that we need a way to tell LSan (and the others)
"yes, you told me this looks wrong, I investigated, it is OK, there
is no need to ever tell me about this again".   Even lint had that
ability from is beginning (30 years ago).

With that these tools can actually be useful (when used properly) - when
an issue is reported, if it is a bug, it gets fixed, if it isn't, then
the tool is told that, and we move on.

  | Feel free to experiment.

Right now, I can't - my build system is unable to build anything llvm
related (its compiler is too old) - which is why I haven't been making
any src changes recently.   Currently I can't build the TOOLS needed to
build everything else.   I need to update it so it can actually
build things again...   In progress, but very slowly.

  | I think we need to specify the definition. Leak is a memory object
  | without a pointer referencing it.

I can accept that as a definition.   But we also need to recognise that
there are no leaks after the program has finished.   And the program has
finished as soon as any of exit()/exec*()/_exit() is called (and succeeds)
or when main() returns.   After that there's nothing left of interest.
Leak detection needs to happen before one of those events occurs.

  | A special type of a leak is referencing memory that is no longer needed
  | and could be freed.

That one will be interesting to find...


  | From the compiler point of view it is only slightly different than any
  | regular "int foo(int, char**)".

>From the compiler's viewpoint you're right, main() is just a function.
But from the overall system's viewpoint it isn't.

  | From sanitizer point of view it is not distinguishable and we cannot
  | (easily) make any conditions based on this.

This is a problem, as return from main, and a call to exit() are
equivalent.   Perhaps we should stop returning from main, and always
explicitly call exit() ?

  | Actually globals are not used in LSan and can be ignored.

I don't know how to interpret that.   In many programs the pointers
to allocated memory are stored in (or found from other data stored in)
global vars.

  | Dynamic linker execution time is a part of execution time of the whole
  | application.

Of course.

  | This implementation of __suppress_leak() takes 9-10 amd64 CPU
  | instructions for each __suppress_leak() call.

What does each of those calls accomplish?   That is, how many of
them are needed?

  | Before getting measurable performance impact of __suppress_leak() we
  | will can go out of memory..

It isn't the call itself which is probably the issue, but traversing
all the data structs (some of which may not have been used in ages, and
might be paged out) in order to find all the pointers to the memory that
is allocated.   Alternatively, we could do:

void *
xmalloc(size_t n)
{
void *p = malloc(n);

if (p == NULL) {
err(1, ""); /* or whatever is appropriate *.
}

__suppress_leak(p);

return p;
}

but is that going to help anything at all, ever?

  | In the context of anything executed in libc/csu/application, even for a
  | hello world application this is negligible.

It isn't the trivial applications that matter, it is the big ones.   And it
isn't the comparison with time currently spent, but how much addional time
is consumed, and whether there is any benefit gained from that.

For the ps example, the actual 

Re: Leak Sanitizer - how to suppress leaks

2019-09-12 Thread Martin Husemann
On Thu, Sep 12, 2019 at 08:21:38PM -0600, Warner Losh wrote:
> When multiple people are doing leak busting, maybe over years, they
> eliminate many false positives so you can focus on the real issues w/o a
> run time penalty. Especially something in the library that comes up
> often... otherwise they get in the way of making progress...

Instead of adding rarely excercised code in the exit path, I would prefer
to mark the allocation with something special so the sanitizer knows
I intend to never free the result and I am fine with "the leak". It could
still report a summary like "10k in 20 objects allocated and intentionaly
not freed", so if this goes up dramatically we may still notice.

Martin


Re: Leak Sanitizer - how to suppress leaks

2019-09-12 Thread Warner Losh
On Thu, Sep 12, 2019, 7:24 PM Simon Burge  wrote:

> Kamil Rytarowski wrote:
>
> > I will revert it, but I am looking for a more generic approach.
> >
> > How about adding ifdef __NO_LEAKS like:
> >
> > #ifdef __NO_LEAKS
> > free(3)?
> > #endif
> >
> > And in lsan/asan/valgrind/etc checks use -D__NO_LEAKS.
>
> Sorry if I'm missing something that has been already explained,
> but why (practically) do we care about memory leaks for a utility
> that is about to finish?
>
> If we're doing some ugly #ifdef dance only when running the
> sanitiser(s), then we haven't actually done anything to "fix"
> the leak in the installed binaries so it seems that there was
> no practical problem that we were trying to solve in the first
> place.
>

When multiple people are doing leak busting, maybe over years, they
eliminate many false positives so you can focus on the real issues w/o a
run time penalty. Especially something in the library that comes up
often... otherwise they get in the way of making progress...

Warner

Cheers,
> Simon.
>


Re: Leak Sanitizer - how to suppress leaks

2019-09-12 Thread Kamil Rytarowski
On 13.09.2019 03:24, Simon Burge wrote:
> Kamil Rytarowski wrote:
> 
>> I will revert it, but I am looking for a more generic approach.
>>
>> How about adding ifdef __NO_LEAKS like:
>>
>> #ifdef __NO_LEAKS
>> free(3)?
>> #endif
>>
>> And in lsan/asan/valgrind/etc checks use -D__NO_LEAKS.
> 
> Sorry if I'm missing something that has been already explained,
> but why (practically) do we care about memory leaks for a utility
> that is about to finish?
> 

I used it in the first place when the resource was no longer needed and
it happened to be before return by an accident.

> If we're doing some ugly #ifdef dance only when running the
> sanitiser(s), then we haven't actually done anything to "fix"
> the leak in the installed binaries so it seems that there was
> no practical problem that we were trying to solve in the first
> place.
> 

It's an optimization to free the resource by exit(2). It is legal in a
POSIX environment, so no need to change this default behavior.

Whenever we affect long-running applications or incomplete procedure
shutdown, we could add free(3) calls unconditionally.

> Cheers,
> Simon.
> 




signature.asc
Description: OpenPGP digital signature


Re: Leak Sanitizer - how to suppress leaks

2019-09-12 Thread Simon Burge
Kamil Rytarowski wrote:

> I will revert it, but I am looking for a more generic approach.
>
> How about adding ifdef __NO_LEAKS like:
>
> #ifdef __NO_LEAKS
> free(3)?
> #endif
>
> And in lsan/asan/valgrind/etc checks use -D__NO_LEAKS.

Sorry if I'm missing something that has been already explained,
but why (practically) do we care about memory leaks for a utility
that is about to finish?

If we're doing some ugly #ifdef dance only when running the
sanitiser(s), then we haven't actually done anything to "fix"
the leak in the installed binaries so it seems that there was
no practical problem that we were trying to solve in the first
place.

Cheers,
Simon.


Re: Leak Sanitizer - how to suppress leaks

2019-09-12 Thread Kamil Rytarowski
On 12.09.2019 21:24, Robert Elz wrote:
> Date:Thu, 12 Sep 2019 15:12:25 +0200
> From:Kamil Rytarowski 
> Message-ID:  <2a6e1fb2-cedc-4a57-750b-45f101be9...@gmx.com>
> 
> 
>   | This proposal is practically equivalent of disabling leak detection at
>   | all and removes the whole purpose.
> 
> No it isn't.   Or rather, it might be, the way you describe LSan to
> work, but if that's what it is doing, then it is close to useless:
> 

Please note that in all arguments about leak sanitization, that it uses
internally the same mechanism as garbage collectors. lsan and libgc (and
others) also server similar purpose.

>   | Leak detection works simply by scanning memory (TLS, stacks, heap) for
>   | pointers to allocations. If there is no longer a reference, than there
>   | is a leak.
> 

I forgot to mention that there might be references to allocated objects
still in at least registers. Certain leaks could be skipped if at least
one thread still references it in a register.

> So the following code
> 
>   struct list {
>   struct list *prev;
>   struct list *next;
>   int age;
>   /* other stuff not relevant here */
>   };
> 
>   struct list *
>   new_list(int age1, int age2)
>   {
>   strust list *l1, *l2;
> 
>   l1 = malloc(sizeof *l1);/* check for NULL elided here */
>   l2 = malloc(sizeof *l2); /* ditto */
> 
>   l1->prev = NULL; l1->next = l2; l1->age = age1;
>   l2->prev = l1; l2->next = NULL; l2->age = age2;
> 
>   if (age1 > age2)
>   return NULL;
> 
>   return l1;
>   }
> 
> has no leak (in the case age1 > age2) ?   Scanning memory would see
> a pointer to *l1 (in *l2), and a pointer to *l2 (in *l1).  Both blocks
> of memory are referenced according to that trivial analysis.   Useless.
> 

LSan can detect indirect leaks:

$ ./test2

=
==14114==ERROR: LeakSanitizer: detected memory leaks

Indirect leak of 24 byte(s) in 1 object(s) allocated from:
#0 0x40ba65 in __interceptor_malloc
/public/llvm/projects/compiler-rt/lib/lsan/lsan_interceptors.cpp:54:3
#1 0x403915 in new_list (/tmp/./test2+0x403915)
#2 0x4039b4 in main (/tmp/./test2+0x4039b4)
#3 0x40381c in ___start (/tmp/./test2+0x40381c)

Indirect leak of 24 byte(s) in 1 object(s) allocated from:
#0 0x40ba65 in __interceptor_malloc
/public/llvm/projects/compiler-rt/lib/lsan/lsan_interceptors.cpp:54:3
#1 0x403907 in new_list (/tmp/./test2+0x403907)
#2 0x4039b4 in main (/tmp/./test2+0x4039b4)
#3 0x40381c in ___start (/tmp/./test2+0x40381c)

SUMMARY: LeakSanitizer: 48 byte(s) leaked in 2 allocation(s).

$ cat test2.c


#include 
#include 

struct list {
struct list *prev;
struct list *next;
int age;
/* other stuff not relevant here */
};

struct list *
new_list(int age1, int age2)
{
struct list *l1, *l2;

l1 = malloc(sizeof *l1);/* check for NULL elided
here */
l2 = malloc(sizeof *l2); /* ditto */

l1->prev = NULL; l1->next = l2; l1->age = age1;
l2->prev = l1; l2->next = NULL; l2->age = age2;

if (age1 > age2)
return NULL;

return l1;
}


int
main(int argc, char **argv)
{
struct list *l;

l = new_list(10,5);

return 0;
}


> The "close to" just above came from there being cases that it can find.
> 
> Because of this, I also looked at the change you pade to "fix" ps.
> You added
> 
>   free(pinfo);
> 
> But *pinfo (a struct pinfo) contains 3 pointers, each of which may
> refer to other malloc'd blocks.   Further, pinfo points to an array
> of those structs.   Because of the memory scan, those pointers (in each
> element of the array) will still have been seen, but when you free(pinfo)
> then those pointers can no longer be accessed (depending upon what the free()
> in use while the sanatiser is running does, they may not still exist
> in memory, or they might, just in a block that is now free).  Anyway
> the struct kinfo_proc2 struct kinfo_lwp and char * (prefix) entries in
> all the struct pinfo's are not being freed.  There was no leak before, but
> your "fix" introduced one (or three * N).
> 

No more leaks were reported, if there are any I will address them.

LSan/NetBSD still needs more work and it is not expected to be perfect.

> I'd suggest reverting the change you made, and instead make pinfo a
> global instead of a local in main - then its value will be seen still
> existing by the memory scan (LSan will be happy) and ps won't be wasting
> (a trivial amount of) time doing an incorrect (as it is now) free(), and
> we'll all be happier.
> 

I will revert it, but I am lo

Re: Leak Sanitizer - how to suppress leaks

2019-09-12 Thread Robert Elz
Date:Thu, 12 Sep 2019 15:12:25 +0200
From:Kamil Rytarowski 
Message-ID:  <2a6e1fb2-cedc-4a57-750b-45f101be9...@gmx.com>


  | This proposal is practically equivalent of disabling leak detection at
  | all and removes the whole purpose.

No it isn't.   Or rather, it might be, the way you describe LSan to
work, but if that's what it is doing, then it is close to useless:

  | Leak detection works simply by scanning memory (TLS, stacks, heap) for
  | pointers to allocations. If there is no longer a reference, than there
  | is a leak.

So the following code

struct list {
struct list *prev;
struct list *next;
int age;
/* other stuff not relevant here */
};

struct list *
new_list(int age1, int age2)
{
strust list *l1, *l2;

l1 = malloc(sizeof *l1);/* check for NULL elided here */
l2 = malloc(sizeof *l2); /* ditto */

l1->prev = NULL; l1->next = l2; l1->age = age1;
l2->prev = l1; l2->next = NULL; l2->age = age2;

if (age1 > age2)
return NULL;

return l1;
}

has no leak (in the case age1 > age2) ?   Scanning memory would see
a pointer to *l1 (in *l2), and a pointer to *l2 (in *l1).  Both blocks
of memory are referenced according to that trivial analysis.   Useless.

The "close to" just above came from there being cases that it can find.

Because of this, I also looked at the change you pade to "fix" ps.
You added

free(pinfo);

But *pinfo (a struct pinfo) contains 3 pointers, each of which may
refer to other malloc'd blocks.   Further, pinfo points to an array
of those structs.   Because of the memory scan, those pointers (in each
element of the array) will still have been seen, but when you free(pinfo)
then those pointers can no longer be accessed (depending upon what the free()
in use while the sanatiser is running does, they may not still exist
in memory, or they might, just in a block that is now free).  Anyway
the struct kinfo_proc2 struct kinfo_lwp and char * (prefix) entries in
all the struct pinfo's are not being freed.  There was no leak before, but
your "fix" introduced one (or three * N).

I'd suggest reverting the change you made, and instead make pinfo a
global instead of a local in main - then its value will be seen still
existing by the memory scan (LSan will be happy) and ps won't be wasting
(a trivial amount of) time doing an incorrect (as it is now) free(), and
we'll all be happier.

Incidentally, what happens with code like:

struct foo {
int a, b, c;
};

int *
make_foo(int a, int b, int c)
{
struct foo *p;

p = malloc(sizeof *p);  /* again NULL check elided here 
*/
p->a = a; p->b = b; p->c = c;

return (&p->b);
}

int * global_ptr;

main()
{
global_ptr = make_foo(1,2,3);

/* ... */

{
struct foo *p;

p = (struct foo *)((intptr_t)global_ptr -
 offsetof(struct foo, b));

p->a++;
}

/* ... */
}

Ugly (and stupid) but not illegal I think.   The more common case is
a list where the back pointers point at the forward pointer, rather
than at the struct itself, so one can do

if ((*(p->back) = p->next) != NULL)
p->next->back = p->back
free(p);

to delete an element from the middle of a list easily - which works
even if the element to be deleted is first or last.   There if the
next field is the first in the struct, the back field will refer to
the malloc's memory block (by accident) but if it isn't, it won't.

  | I have not investigated where are the sh(1) leaks,

As far as I know it doesn't.   What I said is that it does not free
memory it allocates before it exits.  That is not a leak.

  | In sanitizers we cannot overload or do anything special with main()
  | (such as replace it with our own copy).

That wasn't the suggestion.   However, what was assumed a more rational
leak detection algorithm than appears to be in use - I agree, by simply
scanning memory you cannot do anything special with main() as when the
code runs, apparently, main's stack frame has already gone.

But were this done a different way -- the right way is to annotate, in the
code, all assignments to pointers , and add a ref counter to all allocated
memory blocks - when a pointer is assigned, and it points to something
allocated, then incr the ref count.  Whan the value of a pointer is
overwritten, and it used to point to allocated memory, decr the ref count.
If that decrease drops it to 0, then memory has been leaked.  In the
exit code for every 

Re: Leak Sanitizer - how to suppress leaks

2019-09-12 Thread Kamil Rytarowski
On 12.09.2019 10:09, Robert Elz wrote:
> The right change to make however is to teach the santitsers (all of them)
> that exiting (via exit() or a return from main()) frees everything,

This proposal is practically equivalent of disabling leak detection at
all and removes the whole purpose.

Leak detection works simply by scanning memory (TLS, stacks, heap) for
pointers to allocations. If there is no longer a reference, than there
is a leak.

I have not investigated where are the sh(1) leaks, but the pointers are
still accessible in memory. There are few tests failing in regression
test-suite and they related to pointers that are on the stack. There is
apparently a bug in calculating the range of used/valid stack region
somewhere or something that has this red-herring effect. (I would like
to see it fixed, but I am supposed to do other things now and cannot
focus on it.)

On 12.09.2019 10:55, Jason Thorpe wrote:>
>> On Sep 12, 2019, at 11:09 AM, Robert Elz  wrote:
>>
>> To me it seems apparent that the sanatiser is detecting the local
variable
>> in main() go out of scope when main() returns, and so the value it
contains
>> (the pointer to the allocated memory) is lost, and so it is
determined that
>> there is a leak.   For any other function that would often be true,
but for
>> main() it never is.
>
> It seems obvious that the sanitizer should special-case main().
>
> -- thorpej
>

In sanitizers we cannot overload or do anything special with main()
(such as replace it with our own copy). If this would be the case, it
would be simpler to perform some operations in other sanitizers.

main() is the same entity as any regular fun(). Unless we decorate it
with some attributes in .c code, it is not distinguishable.


On 12.09.2019 07:39, Simon Burge wrote:> for the free-just-before-exit
cases?

Leak detection is performed just before the exit.

Technically it is performed from atexit(3) handler that is expected to
be the last one to be executed.

We need to perform this phase in that moment as we need to fire other
atexit handlers, destructors etc.

main() is not really privileged here.

At least with LSan there is an option to perform the end-of-process
check on demand with __lsan_do_leak_check(), it will disable the
atexit(3) handler later on. But it is not much different to adding
workarounds for 1 type of leak detecting software.

On 12.09.2019 10:09, Robert Elz wrote:> If this is what is happening, it
would seem that a relatively cost free
> fix, instead of calling free()

Globals are treated specially in sanitizers as sanitizers generate
constructors and destructors to keep the track of them.

LSan on NetBSD is still distanced from being as good as the Linux
equivalent so it's not a perfect example of presenting how LSan
behaves., I wouldn't assume that globals can store freely pointers. Even
if this is the case today, it would be fixed in future.


On 12.09.2019 10:09, Robert Elz wrote:>   | There is a cost of calling a
dummy function _suppress_leak(), but it is
>   | probably negligible.
>
> No, it isn't.   The way you have described it, that function needs to be
> called for every block of allocated memory that has been allocated, and is
> never going to be freed.   That means either calling this function
immediately
> after all (or much) memory is allocated, even if it turns out that
memory is
> later freed, as often the code does not know what allocations will be
released
> and which will still be active when the program terminates.   Or every
time
> the program is about to exit, it needs to traverse all of its data
structures
> calling this function on all memory that had perviously been allocated.
> Potentially thousands of these dummy function calls.

I have benchmarked in the past that we execute for true(1) or similar
applications at least 1 million instructions on CPU.


$ ./singlestepper true
Total count: 1639915

$ ./singlestepper echo

Total count: 1039124

Calling one 1 dummy function in libc in comparison to that is negligible.

We are talking here about simple programs that are expected to get some
measurable optimization by skipping free(3) calls. Such as sh(1) or ps(1).

In all other cases I consider this style of programming as buggy.

If there are thousands of leaked memory objects than it is not just
simple optimization, but really wrong style of programming. One example
of that is ATF, it randomly picks one set of pointers to free before
exit, and skips others leaking the memory. It is bug.

As I have stated originally, we could remove free(3) from almost every
basesystem program and not many would notice. There is even a C compiler
that performs only allocations and never frees the memory. It is still a
correct UNIX application, regardless of taking like 2GB for building a
hello world application.



signature.asc
Description: OpenPGP digital signature


Re: Leak Sanitizer - how to suppress leaks

2019-09-12 Thread Jason Thorpe


> On Sep 12, 2019, at 11:09 AM, Robert Elz  wrote:
> 
> To me it seems apparent that the sanatiser is detecting the local variable
> in main() go out of scope when main() returns, and so the value it contains
> (the pointer to the allocated memory) is lost, and so it is determined that
> there is a leak.   For any other function that would often be true, but for
> main() it never is.

It seems obvious that the sanitizer should special-case main().

-- thorpej



Re: Leak Sanitizer - how to suppress leaks

2019-09-12 Thread Robert Elz
Date:Thu, 12 Sep 2019 02:58:41 +0200
From:Kamil Rytarowski 
Message-ID:  <373b9331-5306-9797-b4bd-8f6c52683...@gmx.com>

  | I have tested interactive sh(1) with LSan and it does not leak when
  | used.

It doesn't matter what code (if any) you run, there is always memory
allocated in sh that isn't freed when it exits.   Always.   What varies
depending upon what code the shell runs is just how much of it exists.

What this tells me is that the sanatisers aren't detecting unfreed memory
when it is done the way the shell does it.   There are two potential
differences from ps that may be it (perhaps one of them, perhaps both are
needed) - all the shell's allocated memory is referred to from globals,
the variable in question in ps was a local in main().   And second, ps
exits by returning from main(), the shell never does that, it exits via
an explicit call to exit() (or _exit() in one or two cases - the shell forks
a lot so there are several different exit paths depending upon just what
is happening .. all of the forks inherit all the unfreed memory of the
parent shell).

To me it seems apparent that the sanatiser is detecting the local variable
in main() go out of scope when main() returns, and so the value it contains
(the pointer to the allocated memory) is lost, and so it is determined that
there is a leak.   For any other function that would often be true, but for
main() it never is.

If this is what is happening, it would seem that a relatively cost free
fix, instead of calling free() would be simply to make the variable (psinfo)
a global instead of local to main.   That would be a pity, as it makes it
harder to be certain that other code is not fiddling with its value, but
might make this problem go away.

Alternatively, perhaps ps could call exit(eval); instead of doing
return eval; and since one presumes that the sanitisers know that exit()
never returns, then the local variable would never go out of scope, and
hence its memory would not be lost.

The right change to make however is to teach the santitsers (all of them)
that exiting (via exit() or a return from main()) frees everything, and
not to complain about any unfreed memory when that happens (and that local
variables in main effectively have the same lifetime as globals).
This could be an option, so those programs that don't intend to leave
any unfreed memory when they exit could verify that (ideally via a
pragma or magic comment of some kind in the code, rather than yet another
command line option).


  | There is a cost of calling a dummy function _suppress_leak(), but it is
  | probably negligible.

No, it isn't.   The way you have described it, that function needs to be
called for every block of allocated memory that has been allocated, and is
never going to be freed.   That means either calling this function immediately
after all (or much) memory is allocated, even if it turns out that memory is
later freed, as often the code does not know what allocations will be released
and which will still be active when the program terminates.   Or every time
the program is about to exit, it needs to traverse all of its data structures
calling this function on all memory that had perviously been allocated.
Potentially thousands of these dummy function calls.

For something like sh we are not talking about just when the script (or
interactive shell) exits when it is finished, but every time one of its
forked children exits, or calls one of the exec*() family - which also is
effectively exiting the shell (if memory needs to be freed before an exit
it needs to be freed before an exec() as well ... which is tricky when the
args being given to exec() are in malloc'd memory!)

Doing it this way (anything like this way) is simply not feasible.  If there
is to be some new sanatiser specific function call, it needs to be more like
_all_memory_is_free() which simply tells the sanitiser that anything it thinks
is still allocated, should be treated as if it has been freed.

But even that is the wrong way, the right way is to invert that function,
and make it be something like _verify_no_unfreed_memory();

That's a better method than the pragma/magic comment I suggested above to
allow the sanatisers to be told to go check that there is no unallocated
memory, and what's more, can be called any time at all (of course programs
need to be aware that libc functions, like stdio, may have allocated mem,
like FILE buffers, that have not been freed, so use would need to be
cautious).   This still requires the sanatisers to be taught that simply
exiting with unfreed memory is not a problem.

kre



Leak Sanitizer - how to suppress leaks

2019-09-11 Thread Kamil Rytarowski
On 11.09.2019 23:33, Robert Elz wrote:
> Date:Wed, 11 Sep 2019 21:13:24 +0200
> From:Kamil Rytarowski 
> Message-ID:  <6c853bc7-6510-459e-d451-51f988617...@gmx.com>
> 
>   | We have got even fixups in libc for such "nonsense" cases.
> 
> Why?   In 99% (or more) of libc the fixes are relevant, as those functions
> can be called over & over and so should not be discarding memory.
> 
> atexit() is a somewhat special case - if simply calling it leaks, then that
> ought be fixed, if the "leak" is just the data struct used to keep track
> of what needs to be run when the program ends, then not freeing that
> stuff is 100% harmless, and there's no point "fixing" it.
> 

__cxa_finalize() in practice is called on program termination. There are
probably some corner-cases with dlclose(3), but in general we don't
support the interaction between atexit(3) and dlclose(3).

Actually in few cases we do leak memory from libc. One example is in
strerror(3) where we use TSD + destructor. The destructor is never
called on exit(3). It is only called on pthread_exit(0). I don't
consider it as a bug and I would see it in more parts of libc to make
more functions thread safe.

I have marked the strerror(3) cases inside the LSan runtime as a leak to
be ignored.


>   | A workaround would be  ...
> 
> I don't know anything about the sanatisers so can't comment on what
> should be done with them.
> 
> But if you were ever to test sh (and I would guess not just /bn/sh, but
> any shell) for this kind of thing, you'd find that at exit none of the
> user's (or shell's) variables have been freed, the hash table that
> associates command names with the located paths is not freed, aliases are
> not freed, nor defined functions, shell history, key bindings for libedit,
> defined traps, ... and probably much more I cannot remember just now,
> including (if the shell exits via a call to the exit builtin somewhere
> in the script) the internal code tree that is currently being evaluated.
> 

I have tested interactive sh(1) with LSan and it does not leak when
used. I have not tested any non-trivial syntax with it and no shell scripts.

> It would be a lot of code to go free all of that, it would slow down
> shell exit, and all for no benefit at all, as when _exit() eventually
> gets called, everything is returned to the kernel, nothing is lost.
> 
> Further, other than that the shell is exiting, the data structs in
> question all need to be retained, so nothing in them can be freed any time
> before the shell exits (obviously other than when that data is being
> updated, and the old data is freed).
> 
> Does anyone really want to make the shell, or other programs, run slower
> just so someone can say that all memory was nicely (but pointlessly) freed
> before exit ?
> 

I agree that many programs in base could run without freeing the memory
on exit and not many people would notice.

LSan similarly to other leak detectors can detect leaks on the program
termination.

Removal of uninteresting leaks from reports is important as it helps to
notice more serious ones and allows to catch regressions when something
starts to leak.

There are as of now at least 4 leak detectors that could be (not
necessarily ready today) executed on NetBSD:

 - GCC/LLVM LSan
 - Valgrind
 - Dr. Memory/DynamoRIO
 - gperftools

A quick matrix comparison of them is here:

https://github.com/google/sanitizers/wiki/AddressSanitizerComparisonOfMemoryTools

I can see the following solutions to suppress leaks:

 1. use standard free(3)
 2. use standard interfaces from LLVM/GCC sanitizers; potentially
wrapping the interfaces in our headers
 3. add libc functions to suppress leaks

1. This has been committed to ps(1). It is the standard approach and
probably should be preferred unless it slows down the process.

2. There is a trouble here as there is no preprocessor support for
detecting standalone leak sanitizer.

For now we can detect only Address Sanitizer that is built with LSan by
default.

diff --git a/bin/ps/ps.c b/bin/ps/ps.c
index da26fc153601..1e369016041a 100644
--- a/bin/ps/ps.c
+++ b/bin/ps/ps.c
@@ -98,6 +98,14 @@ __RCSID("$NetBSD: ps.c,v 1.91 2018/04/11 18:52:29
christos Exp $");
 #include 
 #include 

+#ifndef __has_feature
+#define __has_feature 0
+#endif
+
+#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
+#include 
+#endif
+
 #include "ps.h"

 /*
@@ -525,6 +533,11 @@ main(int argc, char *argv[])
}
}
}
+
+#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
+   __lsan_ignore_object(pinfo);
+#endif
+
return eval;
 }


A potential approach here is to:

 - add __has_feature() into 

+#ifndef __has_feature
+#define __has_feature 0
+#endif

I already proposed this in the past. It will help to avoid rechecking
__has_feature in every place. It is a Clang/LLVM, not available in GCC.

 - implement __SANITIZE_LEAKS__ in GCC for standalone LSan
 - imp