public inbox for libc-help@sourceware.org
 help / color / mirror / Atom feed
* dlopen: Segfault due to overwriting .so file after it was loaded and loading it again
@ 2020-12-04 10:39 Wendeborn, Jonathan
  2020-12-04 10:44 ` AW: " Wendeborn, Jonathan
  0 siblings, 1 reply; 5+ messages in thread
From: Wendeborn, Jonathan @ 2020-12-04 10:39 UTC (permalink / raw)
  To: libc-help

Hi,

I am a C++ developer but usually programming and debugging on Windows (so please excuse any wrong terms). Now I'm compiling my program on Linux (gcc 9.3.0 on Debian Bullseye with Boost 1.70) for the first time and get a Segfault in my unit tests.
Luckily I was able to write a reproducer and boil it down to my code overwriting the .so file after having it loaded (and unloaded):

#include <boost/filesystem/operations.hpp>
#include <boost/dll/shared_library.hpp>
#include <iostream>

void doit() {
    boost::filesystem::copy_file("~/project/target/references/bin/libSomething.so", "~/project/build/bin/ linux-x86_64-gcc9-debug/ libSomething.so", boost::filesystem::copy_option::overwrite_if_exists);

    boost::dll::shared_library l;
    std::cout << "pre load" << std::endl;
    l.load("./libSomething.so");
    std::cout << "loaded" << std::endl;
}
int main() {
    doit();
    doit();
    return 0;
}

Output:
pre load
loaded
pre load
loaded
Segmentation fault

When removing the copy_file() call everything is fine. The destructor ~shared_library() calls dlclose(), but I suspect the library stays loaded. Overwriting the file creates a new file node and my program wants to load the same library again (at the same location but with a different file node/handle).
This works on Windows because the library is really unloaded after ~shared_library() (otherwise copy_file() would fail as Windows does not support overwriting files in use anyway).

I did debug into dlopen() and think the error gets visible in dl_lookup_x(): In there the strtab and symtab pointers don't have valid pointers the second time, i.e. they have the quite small value from the beginning of elf_get_dynamic_info() (l.51), the l_addr offset from the second part of elf_get_dynamic_info() wasn't added (l.104).
Sure I'm going to rewrite my tests (I'm going to not copy the files at all anymore) but I thought this could be of interest for you.

Best regards,
Jonathan


^ permalink raw reply	[flat|nested] 5+ messages in thread

* AW: dlopen: Segfault due to overwriting .so file after it was loaded and loading it again
  2020-12-04 10:39 dlopen: Segfault due to overwriting .so file after it was loaded and loading it again Wendeborn, Jonathan
@ 2020-12-04 10:44 ` Wendeborn, Jonathan
  0 siblings, 0 replies; 5+ messages in thread
From: Wendeborn, Jonathan @ 2020-12-04 10:44 UTC (permalink / raw)
  To: libc-help

Sorry, seems like this mail slept in my outbox on my second PC for a while. Please ignore it.


- confidential -

-----Ursprüngliche Nachricht-----
Von: Libc-help <libc-help-bounces@sourceware.org> Im Auftrag von Wendeborn, Jonathan via Libc-help
Gesendet: Freitag, 4. Dezember 2020 11:39
An: libc-help@sourceware.org
Betreff: dlopen: Segfault due to overwriting .so file after it was loaded and loading it again

**EXTERNAL EMAIL**

Hi,

I am a C++ developer but usually programming and debugging on Windows (so please excuse any wrong terms). Now I'm compiling my program on Linux (gcc 9.3.0 on Debian Bullseye with Boost 1.70) for the first time and get a Segfault in my unit tests.
Luckily I was able to write a reproducer and boil it down to my code overwriting the .so file after having it loaded (and unloaded):

#include <boost/filesystem/operations.hpp> #include <boost/dll/shared_library.hpp> #include <iostream>

void doit() {
    boost::filesystem::copy_file("~/project/target/references/bin/libSomething.so", "~/project/build/bin/ linux-x86_64-gcc9-debug/ libSomething.so", boost::filesystem::copy_option::overwrite_if_exists);

    boost::dll::shared_library l;
    std::cout << "pre load" << std::endl;
    l.load("./libSomething.so");
    std::cout << "loaded" << std::endl;
}
int main() {
    doit();
    doit();
    return 0;
}

Output:
pre load
loaded
pre load
loaded
Segmentation fault

When removing the copy_file() call everything is fine. The destructor ~shared_library() calls dlclose(), but I suspect the library stays loaded. Overwriting the file creates a new file node and my program wants to load the same library again (at the same location but with a different file node/handle).
This works on Windows because the library is really unloaded after ~shared_library() (otherwise copy_file() would fail as Windows does not support overwriting files in use anyway).

I did debug into dlopen() and think the error gets visible in dl_lookup_x(): In there the strtab and symtab pointers don't have valid pointers the second time, i.e. they have the quite small value from the beginning of elf_get_dynamic_info() (l.51), the l_addr offset from the second part of elf_get_dynamic_info() wasn't added (l.104).
Sure I'm going to rewrite my tests (I'm going to not copy the files at all anymore) but I thought this could be of interest for you.

Best regards,
Jonathan

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: dlopen: Segfault due to overwriting .so file after it was loaded and loading it again
  2020-11-20  6:52 Wendeborn, Jonathan
  2020-11-20  7:17 ` Konstantin Kharlamov
@ 2020-11-20 11:33 ` Florian Weimer
  1 sibling, 0 replies; 5+ messages in thread
From: Florian Weimer @ 2020-11-20 11:33 UTC (permalink / raw)
  To: Wendeborn, Jonathan via Libc-help

* Jonathan via Libc-help Wendeborn:

> The destructor ~shared_library() calls dlclose(), but I suspect the
> library stays loaded. Overwriting the file creates a new file node
> and my program wants to load the same library again (at the same
> location but with a different file node/handle).

This crash happens if the file is truncated on disk and rewritten.
All mapped data is reset to zero if that happens and relocations are
gone.  The kernel does that, there is nothing that glibc can do about
it (Linux does not support MAP_COPY).

So you have two issues here: The library stays loaded (maybe it is
marked as NODELETE, LD_DEBUG=all output logs that in recent glibc
versions), and the file is rewritten in place (and not a “new file
node” is created).

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: dlopen: Segfault due to overwriting .so file after it was loaded and loading it again
  2020-11-20  6:52 Wendeborn, Jonathan
@ 2020-11-20  7:17 ` Konstantin Kharlamov
  2020-11-20 11:33 ` Florian Weimer
  1 sibling, 0 replies; 5+ messages in thread
From: Konstantin Kharlamov @ 2020-11-20  7:17 UTC (permalink / raw)
  To: Wendeborn, Jonathan, libc-help

On Fri, 2020-11-20 at 06:52 +0000, Wendeborn, Jonathan via Libc-help wrote:
> Hi,
>
> I am a C++ developer but usually programming and debugging on Windows (so
> please excuse any wrong terms). Now I'm compiling my program on Linux (gcc
> 9.3.0 on Debian Bullseye with Boost 1.70) for the first time and get a
> Segfault in my unit tests.
> Luckily I was able to write a reproducer and boil it down to my code
> overwriting the .so file after having it loaded (and unloaded):

I can't seem to reproduce it. I modified paths in your testcase as fololows:

    #include <boost/filesystem/operations.hpp>
    #include <boost/dll/shared_library.hpp>
    #include <iostream>

    void doit() {
        boost::filesystem::copy_file("/tmp/libSomething.so", "/tmp/libSomething2.so", boost::filesystem::copy_option::overwrite_if_exists);

        boost::dll::shared_library l;
        std::cout << "pre load" << std::endl;
        l.load("/tmp/libSomething2.so");
        std::cout << "loaded" << std::endl;
    }
    int main() {
        doit();
        doit();
        return 0;
    }

And I build it with

    g++ test.cpp -o a -g3 -O0 -Wall -Wextra -Wsign-conversion -std=c++17 -fsanitize=address -ldl -lboost_filesystem -lboost_system

Running it I get no segfault, just output:

    λ ./a
    pre load
    loaded
    pre load
    loaded

Please try placing the lib into `/tmp/libSomething` and running the app, do you still see crash?



^ permalink raw reply	[flat|nested] 5+ messages in thread

* dlopen: Segfault due to overwriting .so file after it was loaded and loading it again
@ 2020-11-20  6:52 Wendeborn, Jonathan
  2020-11-20  7:17 ` Konstantin Kharlamov
  2020-11-20 11:33 ` Florian Weimer
  0 siblings, 2 replies; 5+ messages in thread
From: Wendeborn, Jonathan @ 2020-11-20  6:52 UTC (permalink / raw)
  To: libc-help

Hi,

I am a C++ developer but usually programming and debugging on Windows (so please excuse any wrong terms). Now I'm compiling my program on Linux (gcc 9.3.0 on Debian Bullseye with Boost 1.70) for the first time and get a Segfault in my unit tests.
Luckily I was able to write a reproducer and boil it down to my code overwriting the .so file after having it loaded (and unloaded):

#include <boost/filesystem/operations.hpp>
#include <boost/dll/shared_library.hpp>
#include <iostream>

void doit() {
    boost::filesystem::copy_file("~/project/target/references/bin/libSomething.so", "~/project/build/bin/ linux-x86_64-gcc9-debug/ libSomething.so", boost::filesystem::copy_option::overwrite_if_exists);

    boost::dll::shared_library l;
    std::cout << "pre load" << std::endl;
    l.load("./libSomething.so");
    std::cout << "loaded" << std::endl;
}
int main() {
    doit();
    doit();
    return 0;
}

Output :


The destructor ~shared_library() calls dlclose(), but I suspect the library stays loaded. Overwriting the file creates a new file node and my program wants to load the same library again (at the same location but with a different file node/handle).
This works on Windows because the library is really unloaded after ~shared_library() (otherwise copy_file() would fail as Windows does not support overwriting files in use anyway).

I did debug into dlopen() and think the error gets visible in dl_lookup_x(): In there the strtab and symtab pointers don't have valid pointers the second time, i.e. they have the quite small value from the beginning of elf_get_dynamic_info() (l.51), the l_addr offset from the second part of elf_get_dynamic_info() wasn't added (l.104).
Sure I'm going to rewrite my tests (I'm going to not copy the files at all anymore) but I thought this could be of interest for you.

Best regards,
Jonathan


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2020-12-04 10:44 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-04 10:39 dlopen: Segfault due to overwriting .so file after it was loaded and loading it again Wendeborn, Jonathan
2020-12-04 10:44 ` AW: " Wendeborn, Jonathan
  -- strict thread matches above, loose matches on Subject: below --
2020-11-20  6:52 Wendeborn, Jonathan
2020-11-20  7:17 ` Konstantin Kharlamov
2020-11-20 11:33 ` Florian Weimer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).