Skip to content

Use __attribute__((visibility("hidden"))) to hide private functions from the dynamic export table #8

@flynn162

Description

@flynn162

In the dynamic library build, Noise_XK_* functions and some private functions (dump, fail, debug, etc.) leaked into the so file's dynamic symbol exports.

readelf -W --dyn-syms /path/to/liboprf-0.4.0/lib/liboprf.so.0
readelf output
[NOTE: UND symbols are omitted]

    58: 0000000000006010   139 FUNC    GLOBAL DEFAULT   14 toprf_Evaluate
    59: 0000000000011960    11 FUNC    GLOBAL DEFAULT   14 Noise_XK_rcode_is_success
    60: 0000000000011830   135 FUNC    GLOBAL DEFAULT   14 Noise_XK_unpack_message_with_auth_level
    61: 0000000000013b20   166 FUNC    GLOBAL DEFAULT   14 Noise_XK_session_get_status
    62: 0000000000004d60     9 FUNC    GLOBAL DEFAULT   14 oprf_KeyGen
    63: 000000000000d760    96 FUNC    GLOBAL DEFAULT   14 Noise_XK_decrypt_and_hash
    64: 000000000001a018     8 OBJECT  GLOBAL DEFAULT   24 log_file
    65: 0000000000013160   460 FUNC    GLOBAL DEFAULT   14 Noise_XK_session_free
    66: 0000000000005a90   296 FUNC    GLOBAL DEFAULT   14 coeff
    67: 000000000000d7c0   224 FUNC    GLOBAL DEFAULT   14 Noise_XK_mix_dh
    68: 0000000000012230   296 FUNC    GLOBAL DEFAULT   14 Noise_XK_serialize_peer_secret
    69: 0000000000012790    25 FUNC    GLOBAL DEFAULT   14 Noise_XK_device_get_static_pub
    70: 00000000000080a0   816 FUNC    GLOBAL DEFAULT   14 tpdkg_start_tp
    71: 0000000000007c40   307 FUNC    GLOBAL DEFAULT   14 tpdkg_peer_input_size
    72: 00000000000126b0   123 FUNC    GLOBAL DEFAULT   14 Noise_XK_device_get_info
    73: 00000000000127b0     7 FUNC    GLOBAL DEFAULT   14 Noise_XK_peer_get_id
    74: 0000000000007f60    13 FUNC    GLOBAL DEFAULT   14 tpdkg_peer_not_done
    75: 0000000000012750     8 FUNC    GLOBAL DEFAULT   14 Noise_XK_device_get_peers_counter
    76: 0000000000012d70  1003 FUNC    GLOBAL DEFAULT   14 Noise_XK_session_create_responder
    77: 00000000000118e0   118 FUNC    GLOBAL DEFAULT   14 Noise_XK_unsafe_unpack_message
    78: 00000000000060a0   237 FUNC    GLOBAL DEFAULT   14 toprf_thresholdcombine
    79: 00000000000127c0   131 FUNC    GLOBAL DEFAULT   14 Noise_XK_peer_get_info
    80: 0000000000004d70   285 FUNC    GLOBAL DEFAULT   14 oprf_Finalize
    81: 0000000000011990   440 FUNC    GLOBAL DEFAULT   14 Noise_XK_device_create
    82: 0000000000007ec0   129 FUNC    GLOBAL DEFAULT   14 tpdkg_peer_set_bufs
    83: 00000000000062f0   668 FUNC    GLOBAL DEFAULT   14 dkg_start
    84: 0000000000014070  1150 FUNC    GLOBAL DEFAULT   14 Noise_XK__session_create_initiator_with_ephemeral
    85: 000000000000cbc0   160 FUNC    GLOBAL DEFAULT   14 tpdkg_recv_err
    86: 0000000000011630    12 FUNC    GLOBAL DEFAULT   14 Noise_XK_uu___is_Stuck
    87: 0000000000011970    12 FUNC    GLOBAL DEFAULT   14 Noise_XK_rcode_is_error
    88: 0000000000012620    43 FUNC    GLOBAL DEFAULT   14 Noise_XK_device_lookup_peer_by_id
    89: 000000000000d170    22 FUNC    GLOBAL DEFAULT   14 Noise_XK_hash
    90: 0000000000007ff0   166 FUNC    GLOBAL DEFAULT   14 tpdkg_tp_set_bufs
    91: 0000000000012650    85 FUNC    GLOBAL DEFAULT   14 Noise_XK_device_lookup_peer_by_static
    92: 000000000000d010     8 FUNC    GLOBAL DEFAULT   14 Noise_XK_bytes_to_nonce
    93: 00000000000116f0    12 FUNC    GLOBAL DEFAULT   14 Noise_XK_uu___is_Conf_level
    94: 0000000000011b50   568 FUNC    GLOBAL DEFAULT   14 Noise_XK_device_create_from_secret
    95: 0000000000006590   503 FUNC    GLOBAL DEFAULT   14 dkg_verify_commitment
    96: 000000000000d060   122 FUNC    GLOBAL DEFAULT   14 Noise_XK_aead_encrypt
    97: 0000000000007f70   117 FUNC    GLOBAL DEFAULT   14 tpdkg_peer_free
    98: 0000000000012740    12 FUNC    GLOBAL DEFAULT   14 Noise_XK_device_sessions_counter_is_saturated
    99: 0000000000006790   268 FUNC    GLOBAL DEFAULT   14 dkg_verify_commitments
   100: 0000000000005760   173 FUNC    GLOBAL DEFAULT   14 oprf_Blind
   101: 0000000000013c90    73 FUNC    GLOBAL DEFAULT   14 Noise_XK_session_get_id
   102: 0000000000012140   240 FUNC    GLOBAL DEFAULT   14 Noise_XK_device_remove_peer
   103: 0000000000011750    12 FUNC    GLOBAL DEFAULT   14 Noise_XK_uu___is_No_level
   104: 0000000000006ab0   326 FUNC    GLOBAL DEFAULT   14 dump
   105: 0000000000011f50   487 FUNC    GLOBAL DEFAULT   14 Noise_XK_device_add_peer
   106: 0000000000012770    25 FUNC    GLOBAL DEFAULT   14 Noise_XK_device_get_static_priv
   107: 0000000000011700    76 FUNC    GLOBAL DEFAULT   14 Noise_XK___proj__Conf_level__item__l
   108: 000000000000d040    25 FUNC    GLOBAL DEFAULT   14 Noise_XK_dh
   109: 0000000000006c00   271 FUNC    GLOBAL DEFAULT   14 fail
   110: 0000000000011780    31 FUNC    GLOBAL DEFAULT   14 Noise_XK_encap_message_p_free
   111: 0000000000006190   345 FUNC    GLOBAL DEFAULT   14 toprf_3hashtdh
   112: 0000000000013330   838 FUNC    GLOBAL DEFAULT   14 Noise_XK_session_write
   113: 0000000000011810    19 FUNC    GLOBAL DEFAULT   14 Noise_XK_pack_message
   114: 000000000000cc60   879 FUNC    GLOBAL DEFAULT   14 tpdkg_cheater_msg
   115: 000000000000d630   220 FUNC    GLOBAL DEFAULT   14 Noise_XK_mix_psk
   116: 0000000000013a40   222 FUNC    GLOBAL DEFAULT   14 Noise_XK_session_compute_next_message_len
   117: 0000000000012730     8 FUNC    GLOBAL DEFAULT   14 Noise_XK_device_get_sessions_counter
   118: 0000000000004e90  1968 FUNC    GLOBAL DEFAULT   14 expand_message_xmd
   119: 0000000000007a30   523 FUNC    GLOBAL DEFAULT   14 tpdkg_tp_peer_msg
   120: 0000000000013bd0   185 FUNC    GLOBAL DEFAULT   14 Noise_XK_session_get_hash
   121: 0000000000011770    11 FUNC    GLOBAL DEFAULT   14 Noise_XK_encap_message_p_is_null
   122: 0000000000006960   336 FUNC    GLOBAL DEFAULT   14 dkg_reconstruct
   123: 0000000000012850    25 FUNC    GLOBAL DEFAULT   14 Noise_XK_peer_get_static
   124: 0000000000013680   955 FUNC    GLOBAL DEFAULT   14 Noise_XK_session_read
   125: 000000000000cfd0    54 FUNC    GLOBAL DEFAULT   14 Noise_XK_lbytes_eq
   126: 0000000000011640    76 FUNC    GLOBAL DEFAULT   14 Noise_XK___proj__Stuck__item___0
   127: 0000000000013ce0   304 FUNC    GLOBAL DEFAULT   14 Noise_XK_session_get_info
   128: 0000000000007f50    13 FUNC    GLOBAL DEFAULT   14 tpdkg_tp_not_done
   129: 000000000000d190   155 FUNC    GLOBAL DEFAULT   14 Noise_XK_mix_hash
   130: 0000000000013fc0   164 FUNC    GLOBAL DEFAULT   14 Noise_XK_session_reached_max_security
   131: 0000000000011980    12 FUNC    GLOBAL DEFAULT   14 Noise_XK_rcode_is_stuck
   132: 000000000000d0e0   132 FUNC    GLOBAL DEFAULT   14 Noise_XK_aead_decrypt
   133: 0000000000005830   186 FUNC    GLOBAL DEFAULT   14 oprf_Unblind
   134: 000000000001a014     4 OBJECT  GLOBAL DEFAULT   24 debug
   135: 00000000000083d0   594 FUNC    GLOBAL DEFAULT   14 tpdkg_start_peer
   136: 0000000000005810    21 FUNC    GLOBAL DEFAULT   14 oprf_Evaluate
   137: 0000000000011e80   197 FUNC    GLOBAL DEFAULT   14 Noise_XK_serialize_device_secret
   138: 0000000000012870  1278 FUNC    GLOBAL DEFAULT   14 Noise_XK_session_create_initiator
   139: 00000000000117a0   102 FUNC    GLOBAL DEFAULT   14 Noise_XK_pack_message_with_conf_level
   140: 0000000000012360   689 FUNC    GLOBAL DEFAULT   14 Noise_XK_deserialize_peer_secret
   141: 0000000000007510   706 FUNC    GLOBAL DEFAULT   14 tpdkg_tp_input_sizes
   142: 0000000000005e10   508 FUNC    GLOBAL DEFAULT   14 toprf_thresholdmult
   143: 000000000000d710    69 FUNC    GLOBAL DEFAULT   14 Noise_XK_encrypt_and_hash
   144: 0000000000011d90   232 FUNC    GLOBAL DEFAULT   14 Noise_XK_device_free
   145: 0000000000007d80   314 FUNC    GLOBAL DEFAULT   14 tpdkg_peer_output_size
   146: 0000000000012760    12 FUNC    GLOBAL DEFAULT   14 Noise_XK_device_peers_counter_is_saturated
   147: 0000000000005bc0   583 FUNC    GLOBAL DEFAULT   14 toprf_create_shares
   148: 00000000000115e0    76 FUNC    GLOBAL DEFAULT   14 Noise_XK___proj__Error__item___0
   149: 000000000000d470   442 FUNC    GLOBAL DEFAULT   14 Noise_XK_kdf
   150: 0000000000013e10    76 FUNC    GLOBAL DEFAULT   14 Noise_XK_session_get_peer_id
   151: 0000000000011760     8 FUNC    GLOBAL DEFAULT   14 Noise_XK___proj__Mkencap_message_p_or_null__item__emp
   152: 0000000000011690    11 FUNC    GLOBAL DEFAULT   14 Noise_XK_uu___is_Auth_level
   153: 00000000000115c0    11 FUNC    GLOBAL DEFAULT   14 Noise_XK_uu___is_Success
   154: 00000000000116a0    75 FUNC    GLOBAL DEFAULT   14 Noise_XK___proj__Auth_level__item__l
   155: 000000000000d230   567 FUNC    GLOBAL DEFAULT   14 Noise_XK_hmac
   156: 0000000000005640   282 FUNC    GLOBAL DEFAULT   14 voprf_hash_to_group
   157: 00000000000144f0   886 FUNC    GLOBAL DEFAULT   14 Noise_XK__session_create_responder_with_ephemeral
   158: 000000000001a008     4 OBJECT  GLOBAL DEFAULT   23 Noise_XK_num_pattern_messages
   159: 000000000000d020    20 FUNC    GLOBAL DEFAULT   14 Noise_XK_dh_secret_to_public
   160: 00000000000118c0    17 FUNC    GLOBAL DEFAULT   14 Noise_XK_unpack_message
   161: 00000000000078d0   352 FUNC    GLOBAL DEFAULT   14 tpdkg_tp_output_size
   162: 00000000000115d0    12 FUNC    GLOBAL DEFAULT   14 Noise_XK_uu___is_Error
   163: 0000000000013e60   345 FUNC    GLOBAL DEFAULT   14 Noise_XK_session_get_peer_info
   164: 000000000000a9b0  8708 FUNC    GLOBAL DEFAULT   14 tpdkg_peer_next
   165: 00000000000068a0   183 FUNC    GLOBAL DEFAULT   14 dkg_finish
   166: 00000000000077e0   233 FUNC    GLOBAL DEFAULT   14 tpdkg_tp_input_size
   167: 0000000000008630  9078 FUNC    GLOBAL DEFAULT   14 tpdkg_tp_next

Possible fix:

  • Add __attribute__((visibility("default"))) at the end of public function declarations.
  • Compile with -fvisibility=hidden which hides all symbols without a visibility attribute.

Ideally I would recommend visibility("protected") over visibility("default"). The blocker is GNU ld doesn't like visibility("protected") inside public header files. When a client passes visibility("protected") to a function declaration, ld (linktime) will look up the function's symbol name across all translation units (the .o files) but it will not look at dynamic libraries. (This means your clients cannot link against your libraries due to mis-used visibility attributes.) You can split the headers into a public version and a dev version (like what I did for my test project), although that would be a huge fix.

Backgrounder:

On Linux, every symbol in a dylib is publicly exported by default. This is a design decision of the GCC toolchain from the 90s.

As another decision decision, ld.so (the runtime loader) does not maintain a graph data structure for your dylib's dependencies. Instead it serializes the graph into an array and uses that same array for every symbol resolve.

This combined linker-loader behavior does not make sense in the age of the internet as people pull a dozen of libraries for every project. An over-exported symbol table increases the risk of symbol clashing. See also this old PHP bug.

P.S. I suggest using this to trace ld.so's resolution order:

LD_DEBUG=reloc:symbols:bindings ./build/test_library 1>&2 2>/tmp/ld-debug.txt

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions