1

I'm creating a small C++ server which is supposed to handle requests in an infinite loop like.

void Server::start_accept_loop(int server, SSL_CTX* ctx){
    sockaddr_in incoming_addr;
    socklen_t len = sizeof(incoming_addr);
    int client_sock;
    SSL* ssl;
    while(Server::is_accepting){
        client_sock = accept(server, (sockaddr*)&incoming_addr, len);
        ssl = SSL_new(ctx);
        SSL_set_fd(ssl, client_sock);
        if (SSL_accept(ssl) == -1){
            printf("Unable to accept ssl connection\n");
        }else{
            std::shared_ptr<Client> client (new Client(ssl));
            std::thread th (background_client, client);
        }
    }
}
void Server::background_client(std::shared_ptr<Client> client_ptr){
    std::shared_ptr<Client> client = client_ptr;
    int request = 0;
    request = client->get_request();
    ...
}

I have a few questions actually.

  1. When the created client shared_ptr goes out of scope after the thread creation does it decrement reference count and deallocate the object. It is a race condition with the new thread or is the reference count incremented at the std::thread th (background_client, client); line. Basically, I'm wondering if I need to wait some time before the while loop goes out of scope and create a new shared_ptr or if the reference count is incremented immediately?
  2. My second question is about the SSL structure pointer. If the underlying client_sock and incoming_addr objects go out of scope without having been created dynamically on the heap, is it a problem for the SSL connection which probably relies on these or does the SSL pointer contain a conform dynamic copy of all this information?
  3. Is there anything else I should know?

Thank you.

user123
  • 2,510
  • 2
  • 6
  • 20
  • 2
    Questions 1 and 2 are really unrelated and I think they should be separate posts. – Nate Eldredge Dec 20 '20 at 17:33
  • They are both scope questions. I think they are related. I couldn't create 2 posts with the same code. – user123 Dec 20 '20 at 17:37
  • 1
    Well, in the code above you create thread and then immediately leave the scope. It will throw an exception immediately. You ought to use `detach()` if you don't intent to wait till it's completion. In general, you should use a better framework for multithreading than just creating threads for managing connections as you should properly and safely exit the application. If not all detached threads finished their work then it's a problem. And what you want to about all tasks and quit but graceful? What to do then? – ALX23z Dec 20 '20 at 18:51
  • Yes I had to detach the thread for it to work. I don't understand your other questions though. It is a server so it ought to run all the time except for maintenance etc. So when in use I don't mind about threads being detached. – user123 Dec 20 '20 at 19:43

3 Answers3

1
  1. The reference count is incremented when the new shared_ptr is instantiated (i.e., at the time of the call to the thread ctor), so I can't imagine the Client object would be deallocated unless the thread was destroyed, or until Server::background_client returned.

  2. Probably impossible to tell without looking at the Client implementation. I would say that's probably that class' problem to make sure the SSL context stays around, but again it's hard to say without looking at the rest of the code.

osuka_
  • 484
  • 5
  • 15
  • The Client implementation is simple. It is just using the SSL context and deallocating it in the destructor. Maybe it is unrelated but I thought I had some segfault problems because the incoming_addr and client_sock were going out of scope. Maybe it wasn't related. After all the SSL structure is created dynamically. – user123 Dec 20 '20 at 17:46
  • Thank you though. As I understand it, the ctor of the thread is called inline in the loop and the reference count is incremented immediately. – user123 Dec 20 '20 at 17:47
1

#2 looks fine to me.

incoming_addr is passed by pointer to the system call accept which only reads its data and won't store the pointer. The kernel will keep its own copy of the information, it doesn't need to keep referring back to yours. Thus incoming_addr does not need to stay alive after the call to accept.

client_sock is an int and is passed by value to SSL_set_fd (this is a C API and so can't take references except as pointers, but you can also confirm this by reading the docs). So the socket fd is necessarily copied to be stored in the SSL object. The code inside SSL_set_fd never even knows the address of your client_sock variable and so it can't possibly have a reference to it; thus client_sock does not need to stay alive after the function call.

The SSL object itself will stay alive until SSL_freed, of course.

Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82
1

When the created client shared_ptr goes out of scope after the thread creation does it decrement reference count and deallocate the object. It is a race condition with the new thread or is the reference count incremented at the std::thread th (background_client, client); line. Basically, I'm wondering if I need to wait some time before the while loop goes out of scope and create a new shared_ptr or if the reference count is incremented immediately?

The reference count will be decremented when the shared_ptr goes out of scope, yes. But the Client object will not be destroyed yet, because the shared_ptr is being copied into the thread, incrementing the reference count immediately. The thread has an active reference, so the object will not be destroyed until the thread ends.

My second question is about the SSL structure pointer. If the underlying client_sock and incoming_addr objects go out of scope without having been created dynamically on the heap, is it a problem for the SSL connection which probably relies on these or does the SSL pointer contain a conform dynamic copy of all this information?

You are not providing the SSL struct with the sockaddr_in that you created, so there is no worry about that going out of scope. If OpenSSL needs the address information, it will just retrieve its own copy from the socket object. As for your client_sock variable, it is just an integer, it gets copied around by value, so there is no worry about that going out of scope, either. The SSL struct will hold its own copy of the descriptor, taking ownership of it, and the underlying socket object that the descriptor refers to will not be freed until the descriptor is explicitly close()’d when the SSL struct is destroyed by SSL_free().

Is there anything else I should know?

The code shown is not checking if accept() fails. And it is leaking the socket object and SSL struct if the SSL/TLS handshake fails.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Thank you. Yes the SSL_accept is tcheked and ssl struct freed if ever the connection fails. I didn't add it in the question. The accept call is checked as well. – user123 Dec 20 '20 at 19:44