Re: Leak Sanitizer - how to suppress leaks
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
> 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
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
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