Bug #120489 MgmApiSession queries peer address from a moved-from NdbSocket

Submitted: 17 May 16:24
Reporter: S F. Email Updates:
Status: Open Impact on me:
None 
Category:MySQL Cluster: Cluster (NDB) storage engine Severity:S3 (Non-critical)
Version:MySQL 9.6.0, commit 447eb26e094b444a88c5 OS:Any
Assigned to: CPU Architecture:Any
Tags: mgmapi, ndb

[17 May 16:24] S F.
Description:
MgmApiSession::MgmApiSession() moves the accepted NdbSocket &&sock parameter into m_secure_socket, then later calls ndb_getpeername(sock.ndb_socket(), &addr) on the moved-from parameter.

NdbSocket move construction swaps the native socket handle into the destination and leaves the source object default-invalid. Therefore sock.ndb_socket() returns an invalid socket after the move. On POSIX this results in getpeername(-1, ...), which fails with EBADF.

As a result, the constructor does not resolve the peer address and m_name remains initialized as "unknown:0", making management API session logging/diagnostics misleading.

Relevant code path:

```cpp
MgmApiSession::MgmApiSession(class MgmtSrvr &mgm, NdbSocket &&sock,
                              Uint64 session_id)
    : SocketServer::Session(m_secure_socket),
      m_secure_socket(std::move(sock)),
      ...
      m_name("unknown:0") {
  ...
  ndb_sockaddr addr;
  if (ndb_getpeername(sock.ndb_socket(), &addr) == 0) {
    ...
    m_name.assfmt("%s", sockaddr_string);
  }
}
```

How to repeat:
The following simplified program reproduces the ownership transfer issue using the same operation sequence as MgmApiSession: move an NdbSocket, then call ndb_getpeername() on the moved-from object versus the moved-to object.

```cpp
#include "util/NdbSocket.h"

#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>

#include <iostream>

int main() {
  int listener = ::socket(AF_INET, SOCK_STREAM, 0);

  sockaddr_in addr{};
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
  addr.sin_port = 0;

  ::bind(listener, reinterpret_cast<sockaddr *>(&addr), sizeof(addr));
  ::listen(listener, 1);

  socklen_t addr_len = sizeof(addr);
  ::getsockname(listener, reinterpret_cast<sockaddr *>(&addr), &addr_len);

  int client_fd = ::socket(AF_INET, SOCK_STREAM, 0);
  ::connect(client_fd, reinterpret_cast<sockaddr *>(&addr), addr_len);

  int server_fd = ::accept(listener, nullptr, nullptr);
  ::close(listener);

  NdbSocket sock(ndb_socket_create_from_native(server_fd));
  NdbSocket m_secure_socket(std::move(sock));

  ndb_sockaddr from_moved_param;
  errno = 0;
  int moved_result = ndb_getpeername(sock.ndb_socket(), &from_moved_param);
  int moved_errno = errno;

  ndb_sockaddr from_member;
  errno = 0;
  int member_result =
      ndb_getpeername(m_secure_socket.ndb_socket(), &from_member);
  int member_errno = errno;

  std::cout << "moved-from sock valid: " << sock.is_valid() << "\n";
  std::cout << "m_secure_socket valid: " << m_secure_socket.is_valid() << "\n";
  std::cout << "ndb_getpeername(sock.ndb_socket()) result: "
            << moved_result << ", errno: " << moved_errno << "\n";
  std::cout << "ndb_getpeername(m_secure_socket.ndb_socket()) result: "
            << member_result << ", errno: " << member_errno << "\n";

  ::close(client_fd);
  ::close(m_secure_socket.release_native_socket());
}
```

Observed output:

moved-from sock valid: 0
m_secure_socket valid: 1
ndb_getpeername(sock.ndb_socket()) result: 1, errno: 9
ndb_getpeername(m_secure_socket.ndb_socket()) result: 0, errno: 0

errno: 9 is EBADF, confirming that sock.ndb_socket() is invalid after std::move(sock), while m_secure_socket.ndb_socket() is valid.

Suggested fix:
Use the session-owned socket after the move:

```cpp
ndb_sockaddr addr;
if (ndb_getpeername(m_secure_socket.ndb_socket(), &addr) == 0) {
  char addr_buf[NDB_ADDR_STRLEN];
  char *addr_str = Ndb_inet_ntop(&addr, addr_buf, sizeof(addr_buf));
  char buf[512];
  char *sockaddr_string =
      Ndb_combine_address_port(buf, sizeof(buf), addr_str, addr.get_port());
  m_name.assfmt("%s", sockaddr_string);
}
```

Portions of this website are copyright © 2001, 2002 The PHP Group

Page generated in 0.038 sec. using MySQL 8.4.7-u6-cloud

Timestamp references displayed by the system are UTC.
Content reproduced on this site is the property of the respective copyright holders.
It is not reviewed in advance by Oracle and does not necessarily represent the opinion of Oracle or any other party.