Server eats 100% CPU all the time [FreeBSD14.4]

Hello,

I tried to run a ts3server on a FreeBSD system. The precompiled binaries for FreeBSD seem to support FreeBSD 13.5 onwards. The server works, can deal with clients and all but its constantly at 100% CPU usage which is a bit boring. I read in a different thread that FreeBSD will be not supported anymore but the version from the port system is not too old:

/usr/local/libexec/ts3server: ELF 64-bit LSB executable, x86-64, version 1 (FreeBSD), dynamically linked, interpreter /libexec/ld-elf.so.1, for FreeBSD 13.5, FreeBSD-style, stripped

if possible please provide an updated version.

thanks

what resources are allocated, is it enough to run the server? I know it usually take like nothing, but still it’s good to know which specs are used for this instance

almost no resources are bound on this system:

CPU: 12.9% user, 0.0% nice, 0.0% system, 0.0% interrupt, 87.1% idle
Mem: 273M Active, 1580M Inact, 1369M Laundry, 12G Wired, 16G Free
ARC: 5574M Total, 1567M MFU, 1588M MRU, 213K Anon, 84M Header, 2328M Other
1710M Compressed, 4014M Uncompressed, 2.35:1 Ratio

the 12.5% (1 full CPU) is the ts3server process. the load on the machine (without TS3) is below 0.1 … so its clearly an issue with the ts3server.

PID USERNAME THR PRI NICE SIZE RES STATE C TIME WCPU COMMAND
39458 TeamSpeak 21 141 0 82M 26M CPU7 7 180:35 100.39% ts3server

memory consumption seems normal for the process…

nevermind. I meanwhile installed a debian VM within bhyve hypervisor on the same box which runs just fine and doesnt eat up 100% CPU all the time. I need it anyway for other gameing-related services…

I am reporting a reproducible cryptographic arithmetic correctness and availability bug in the currently distributed TeamSpeak 3 Server 3.13.8 FreeBSD AMD64 package. This deserves a maintenance fix even if long-term FreeBSD support has ended.

The server can consume a CPU core during automatic SSH host-key generation. Supplying an externally generated, readable RSA key avoids that generation step but does not fix SSH Query: key exchange can fail because the bundled library cannot correctly sign the session identifier. The library’s own fixed-vector modular-exponentiation self-test fails, independently of Query credentials, networking, database contents or a running TS3 instance.

I have isolated an erroneous Montgomery-reduction calculation in the shipped libts3_ssh.so. A narrowly targeted, byte-guarded correction makes the failing arithmetic test pass, restores RSA-SHA512 SSH key exchange, and allows the actual TS3 server process to generate its default RSA4096 host key successfully.

Support policy and request

I have read the previous reply about discontinuing future FreeBSD support. However, the official download page still offers FreeBSD Server 3.13.8 as of 19 October 2026.

A narrowly scoped maintenance rebuild of that existing artifact would address this serious defect while leaving the broader platform-support policy unchanged. Please provide either:

  • a corrected FreeBSD server package or an ABI-compatible replacement libts3_ssh.so;
  • or the matching library source changes, configuration and build instructions needed to produce such a replacement.

Please also run the bundled MPI self-tests in release validation. A deterministic arithmetic failure inside a cryptographic dependency should not remain in a downloadable release.

The other report of sustained FreeBSD CPU usage may be related, but I cannot establish that it has the same cause without comparing its binary hash/self-test result.

Affected artifact and environment

Tested on FreeBSD 15.1-RELEASE-p5 AMD64, with TS3 3.13.8 installed via the FreeBSD port (teamspeak3-server-3.13.8,1). The port installs the vendor binaries. More importantly, the same defective code is present in the official archive and its failure is independently reproduced from that archive; it is not limited to one installed or stripped copy.

Official archive:
https://files.teamspeak-services.com/releases/server/3.13.8/teamspeak3-server_freebsd_amd64-3.13.8.tar.bz2

Archive SHA256:
2bfd96ebfa1fca0c013a0a03d1741c0dc6d79e7c501a2fffb6248fdea58d4315
Unstripped libts3_ssh.so SHA256:
52cece666ac9e0798b2834fb080d51e378e8b86bbf839ed171cab0cde62f0a12
Original executable PT_LOAD SHA256:
a10e5c11186344d73c4be687487c6908fdd6e7d88eaaaa00e0bd722d2ba75c59

The FreeBSD library contains libssh_0.8.4 and mbed TLS 2.7.16 version strings. These strings identify bundled components; they do not mean every build of upstream libssh 0.8.4 or Mbed TLS 2.7.16 has this defect.

Minimal arithmetic evidence

Calling the library’s built-in MPI self-test with the exact executable code hash verified first gives:

MPI test #1 (mul_mpi): passed
MPI test #2 (div_mpi): passed
MPI test #3 (exp_mod): failed
Unexpected error, return code = 00000001
MPI_SELF_TEST_RETURN=1

The self-test entry is image-relative RVA 0xbd3d0, with the upstream declaration int mbedtls_mpi_self_test(int verbose). It was identified using test strings/vectors and function-range information. It is not an exported stable libssh API, so the reproducer refuses unknown executable hashes before calling it.

This failure was observed on real FreeBSD and independently reproduced by replaying the exact official ELF instructions. No private key, password or network session is needed for this fixed-vector test. I include a small native reproducer below.

Exact emitted-code defect

The Montgomery multiplication helper starts at RVA 0xba240. A multiply-accumulate step has already updated d[0]. The sequence at RVA 0xba64a then adds the same product again while calculating the reduction factor:

mov  rbx, [r12]      ; B[0]
imul rbx, r10        ; u0 * B[0]
add  rbx, [r14]      ; d[0] already contains that product

It should use the already updated d[0] here. The tested workaround replaces that 11-byte sequence with a load and padding NOPs:

Before: 49 8b 1c 24 49 0f af da 49 03 1e
After:  49 8b 1e 90 90 90 90 90 90 90 90
        mov rbx,[r14] followed by padding

For the official unstripped file the offset is 0xb964a. Do not apply that file offset blindly to another build. The attached patcher derives the file offset from the ELF segment and checks the entire original executable segment, exact instruction bytes and complete resulting file hash.

The instruction ordering is consistent with an inline-assembly memory-side-effect/compiler-reordering problem, but I do not have the vendor’s complete build source/options. The concrete emitted arithmetic error is confirmed; a particular compiler or upstream-source-level cause is not.

Before/after validation

With that correction, real FreeBSD testing passed all five built-in tests: multiplication, division, modular exponentiation, modular inverse and gcd. Independent instruction replay also checked 616 Montgomery outputs against integer reference calculations and 104 additional fixed-seed cases, spanning 13 limb lengths up to 4096 bits, including boundary and aliasing cases. This is not exhaustive cryptographic validation.

In isolated library tests, both an existing RSA key and a newly generated RSA4096 key completed pinned-host-key RSA-SHA512 SSH key exchange. On the actual server, after installing the corrected library and retiring the previous manual key, TS3 itself generated RSA4096 and started normally. Startup plus key readiness took about five seconds; that is not a standalone key-generation benchmark.

The generated FreeBSD key initially had mode 0644; it was restricted to 0600 with the correct service owner/group. Real IPv4 and IPv6 SSH Query connections then verified the new host key and reached password authentication. In a short post-connection CPU sample, displayed TS3 threads were at 0.00–0.06%, with the final two system samples 99.8% and 99.9% idle. Full Query account login, long-term stability and restart persistence are not claimed from these results.

Other platforms: distinguish results from assumptions

Official 3.13.8 platform Evidence available Result for this specific failure
FreeBSD AMD64 Real server/library tests and instruction replay Confirmed; fixed by the guarded correction above
Windows AMD64 Native temporary server test Automatic RSA4096 generation and pinned RSA-SHA512 handshake passed; this key-generation failure was not reproduced
Linux AMD64 Exact library MPI self-test in instruction replay All five passed; no native Linux server integration test performed
macOS Intel Exact Mach-O library MPI self-test in instruction replay All five passed; no native macOS server integration test performed

The emulator mocks allocation/memory/stdio helpers; the macOS run additionally supplies compiler-runtime unsigned 128-bit division/modulo helpers using integer arithmetic. It does not emulate a complete OS, network service or RSA session. Therefore these results do not certify that other platforms are bug-free. A separate Windows RSA-SHA256 signature failure was observed and remains outside the demonstrated FreeBSD Montgomery fix.

Unofficial, version-pinned workaround

An official compatible rebuild is preferable. This is not a confirmed exploitable-vulnerability/CVE claim: the demonstrated impacts are key-generation CPU consumption, failing arithmetic and broken SSH Query signatures. I have not established key recovery, authentication bypass or remote code execution.

The script below only creates a separate corrected library. It does not download anything, replace the live library, stop/restart a server or touch keys/configuration. It refuses different code, already patched input, modified non-code contents, in-place output and overwriting an existing output file. It supports exactly the identified official and installed/stripped layouts.

Byte-guarded offline patcher (Python 3.9+, standard library only)
"""Create a separate, hash-guarded correction of the known TS3 3.13.8 library.

Python 3.9+. No dependencies, downloads, service control or private-key access.
Without --output this only checks eligibility. Never edits the input in place.
This is an unofficial workaround for exactly the identified FreeBSD AMD64 build.
"""
import argparse
import hashlib
import os
from pathlib import Path
import struct

ORIGINAL_RX = "a10e5c11186344d73c4be687487c6908fdd6e7d88eaaaa00e0bd722d2ba75c59"
CORRECTED_RX = "e654636a42fcf409df134d5a168a3a13e5336f2ecdc3f50af1414f75e80d1370"
CORRECTED_FILES = {
    "08a76c8d781bc9f07cb4dd74f248adb65aa5034268681e3687e189a961e10e9f",
    "081f7d87ba920572d329e5dd0da52e23a8c10239d6ca7feee427f7504258e194",
}
BEFORE = bytes.fromhex("49 8b 1c 24 49 0f af da 49 03 1e")
AFTER = bytes.fromhex("49 8b 1e 90 90 90 90 90 90 90 90")


def digest(data):
    return hashlib.sha256(data).hexdigest()


def corrected_copy(data):
    if len(data) < 64 or len(data) > 16 * 1024 * 1024:
        raise ValueError("Unsupported input size")
    h = struct.unpack_from("<16sHHIQQQIHHHHHH", data)
    if (h[0][:7] != b"\x7fELF\x02\x01\x01" or h[1] != 3 or h[2] != 62
            or h[9] != 56 or not 0 < h[10] <= 64
            or h[5] > len(data) or h[10] * 56 > len(data) - h[5]):
        raise ValueError("Expected the known ELF64 little-endian AMD64 shared library")
    candidates = []
    for i in range(h[10]):
        ph = struct.unpack_from("<IIQQQQQQ", data, h[5] + i * 56)
        if ph[0] == 1 and ph[1] == 5 and ph[3] == 0x62410 and ph[5] == 0x94FE0:
            candidates.append(ph)
    if len(candidates) != 1:
        raise ValueError("Unexpected executable segment layout")
    ph = candidates[0]
    offset, size = ph[2], ph[5]
    if offset > len(data) or size > len(data) - offset:
        raise ValueError("Executable segment is outside the file")
    if digest(data[offset:offset + size]) != ORIGINAL_RX:
        raise ValueError("Original executable hash mismatch; do not force this patch")
    position = offset + 0xBA64A - ph[3]
    if data[position:position + len(BEFORE)] != BEFORE:
        raise ValueError("Instruction bytes mismatch")
    result = bytearray(data)
    result[position:position + len(BEFORE)] = AFTER
    result = bytes(result)
    if digest(result[offset:offset + size]) != CORRECTED_RX:
        raise ValueError("Corrected executable hash mismatch")
    if digest(result) not in CORRECTED_FILES:
        raise ValueError("Whole-file result is not one of the two identified layouts")
    return result


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("input", type=Path)
    parser.add_argument("--output", type=Path, help="create a NEW separate file; never overwrite")
    args = parser.parse_args()
    if args.output is not None and args.input.resolve() == args.output.resolve():
        parser.error("Input and output must be different paths")
    if args.input.stat().st_size > 16 * 1024 * 1024:
        parser.error("Unsupported input size")
    original = args.input.read_bytes()
    try:
        corrected = corrected_copy(original)
    except ValueError as exc:
        parser.error(str(exc))
    print("ORIGINAL_FILE_SHA256=" + digest(original))
    print("CORRECTED_FILE_SHA256=" + digest(corrected))
    print("CORRECTED_RX_SHA256=" + CORRECTED_RX)
    if args.output is None:
        print("ELIGIBLE_CHECK_ONLY=1")
        return
    fd = os.open(args.output, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
    with os.fdopen(fd, "wb") as stream:
        stream.write(corrected)
        stream.flush()
        os.fsync(stream.fileno())
    if digest(args.output.read_bytes()) != digest(corrected):
        raise RuntimeError("Output verification failed; do not install the output")
    print("SEPARATE_CORRECTED_FILE_CREATED=1")


if __name__ == "__main__":
    main()

Example, in an extracted official package directory:

python3 patch_freebsd_ts3_ssh.py ./libts3_ssh.so
python3 patch_freebsd_ts3_ssh.py ./libts3_ssh.so --output ./libts3_ssh.corrected.so

Expected corrected hashes:

Corrected executable PT_LOAD:
e654636a42fcf409df134d5a168a3a13e5336f2ecdc3f50af1414f75e80d1370
Corrected complete official/unstripped file:
08a76c8d781bc9f07cb4dd74f248adb65aa5034268681e3687e189a961e10e9f
Corrected complete tested installed/stripped file:
081f7d87ba920572d329e5dd0da52e23a8c10239d6ca7feee427f7504258e194

Installation must use your service’s own stop/start procedure and library location, preserve a verified original-library recovery copy, and preserve existing host keys unless you deliberately choose to replace them. This is a package-owned file and upgrades can overwrite it. The workaround is not a drop-in replacement for arbitrary versions/builds and is not an audit of the whole old dependency stack.

Native FreeBSD fixed-vector reproducer (no private keys or running server required)

Save as ts3_freebsd_mpi_repro.c:

/* FreeBSD-only public diagnostic for the pinned TS3 3.13.8 vendor library.
 * No keys, database, credentials, networking, or service changes.
 * A complete executable-segment hash gates the internal self-test address.
 */
#include <sys/types.h>
#include <dlfcn.h>
#include <elf.h>
#include <sha256.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

static void deadline(int sig) {
    (void)sig;
    (void)write(2, "BACKEND_PROBE_TIMEOUT\n", sizeof("BACKEND_PROBE_TIMEOUT\n") - 1);
    _exit(124);
}
int main(int argc, char **argv) {
    if (argc != 2) { fputs("Usage: mpi-repro /absolute/path/libts3_ssh.so\n", stderr); return 2; }
    setbuf(stdout, NULL);
    signal(SIGALRM, deadline);
    alarm(10);
    void *lib = dlopen(argv[1], RTLD_NOW | RTLD_LOCAL);
    if (!lib) { fprintf(stderr, "DLOPEN_ERROR: %s\n", dlerror()); return 2; }
    void *rng_symbol = dlsym(lib, "ssh_get_random");
    void *init_symbol = dlsym(lib, "ssh_init");
    void *finish_symbol = dlsym(lib, "ssh_finalize");
    if (!rng_symbol || !init_symbol || !finish_symbol) {
        fputs("MISSING_SYMBOL\n", stderr); return 2;
    }
    Dl_info info;
    if (!dladdr(rng_symbol, &info) || !info.dli_fbase) {
        fputs("LIBRARY_BASE_ERROR\n", stderr); return 2;
    }
    uintptr_t base = (uintptr_t)info.dli_fbase;
    const Elf64_Ehdr *header = (const Elf64_Ehdr *)base;
    if (memcmp(header->e_ident, ELFMAG, SELFMAG) != 0 ||
        header->e_ident[EI_CLASS] != ELFCLASS64 || header->e_machine != EM_X86_64 ||
        header->e_phentsize != sizeof(Elf64_Phdr) || header->e_phnum > 64 ||
        header->e_phoff > 0x1000 ||
        header->e_phoff + header->e_phnum * sizeof(Elf64_Phdr) > 0x1000 ||
        (uintptr_t)rng_symbol - base != 0x8c8b0) {
        fputs("UNSUPPORTED_LIBRARY_LAYOUT\n", stderr); return 2;
    }
    const Elf64_Phdr *segments = (const Elf64_Phdr *)(base + header->e_phoff);
    int matched = 0;
    for (unsigned int i = 0; i < header->e_phnum; ++i) {
        if (segments[i].p_type == PT_LOAD && segments[i].p_flags == (PF_R | PF_X) &&
            segments[i].p_vaddr == 0x62410 && segments[i].p_filesz == 0x94fe0 &&
            segments[i].p_memsz >= segments[i].p_filesz) matched = 1;
    }
    if (!matched) { fputs("UNSUPPORTED_EXECUTABLE_SEGMENT\n", stderr); return 2; }
    char hash[65];
    if (!SHA256_Data((const unsigned char *)(base + 0x62410), 0x94fe0, hash)) {
        fputs("CODE_HASH_ERROR\n", stderr); return 2;
    }
    printf("VENDOR_EXECUTABLE_SHA256=%s\n", hash);
    if (strcmp(hash, "a10e5c11186344d73c4be687487c6908fdd6e7d88eaaaa00e0bd722d2ba75c59") != 0 &&
        strcmp(hash, "e654636a42fcf409df134d5a168a3a13e5336f2ecdc3f50af1414f75e80d1370") != 0) {
        fputs("VENDOR_CODE_MISMATCH: internal test NOT called\n", stderr); return 2;
    }
    puts("VENDOR_CODE_VERIFIED=1");
    int (*init)(void), (*finish)(void), (*mpi_test)(int);
    _Static_assert(sizeof(init) == sizeof(init_symbol), "FreeBSD pointer size");
    memcpy(&init, &init_symbol, sizeof(init));
    memcpy(&finish, &finish_symbol, sizeof(finish));
    void *test_address = (void *)(base + 0xbd3d0);
    _Static_assert(sizeof(mpi_test) == sizeof(test_address), "FreeBSD pointer size");
    memcpy(&mpi_test, &test_address, sizeof(mpi_test));
    int rc = init();
    printf("SSH_INIT=%d\n", rc);
    if (rc != 0) return 1;
    puts("MPI_SELF_TEST_BEGIN");
    /* Identified from .eh_frame FDE + test-vector/string cross references;
     * prototype confirmed in Mbed TLS 2.7.16 bignum.c. No source copied here.
     */
    rc = mpi_test(1);
    printf("MPI_SELF_TEST_RETURN=%d\n", rc);
    finish();
    dlclose(lib);
    alarm(0);
    return rc == 0 ? 0 : 1;
}

From a directory containing the original or corrected library:

cc -std=c11 -O2 -Wall -Wextra -o mpi-repro ts3_freebsd_mpi_repro.c -lmd
ulimit -c 0
./mpi-repro "$(realpath ./libts3_ssh.so)"
./mpi-repro "$(realpath ./libts3_ssh.corrected.so)"

The original should fail at exp_mod; the corrected copy should pass all five tests. The underlying native test call/hash gates were exercised on real FreeBSD. This public command-line wrapper was statically checked but has not separately been compiled/run there; please report any build/runtime difference rather than bypassing a hash guard.

Investigation and drafting provenance

This work was carried out collaboratively. Most of the binary analysis, diagnostic and patch tooling, local comparison experiments, and report drafting were completed by OpenAI Codex, an AI assistant, under my direction. I performed the FreeBSD checks and deployment and supplied the observed terminal output. The real-system observations, instruction-emulation results and unverified conclusions are distinguished above; the AI contribution does not replace those validation boundaries.

I am sharing code and artifact identifiers, not vendor binaries, private keys, account details, server addresses or database files. Please confirm whether TeamSpeak can provide a corrected official FreeBSD artifact or compatible library rebuild.

As mentioned in the linked thread, unfortunately, we won’t dedicate any further resources to FreeBSD. If a new update is released, it will be because it is in the build pipeline; otherwise, there won’t be a new release.