Skip to content

# [Bug] PLC task crashes with signal 11 (SIGSEGV) on Python Function Block shared-memory init, even with a minimal single-block, arm64 Docker #167

Description

@alkoval72

Environment

  • OpenPLC Runtime: v4.1.10, image ghcr.io/autonomy-logic/openplc-runtime@sha256:1650a50d1a3e9c75cdd548c06866fc328aac1268af9b83c0313a4ab96500325d, revision bf82b1b661fd95c9899969f629d692b77b4e1454, built 2026-08-11T20:23:44.547Z (latest available at time of report)
  • Architecture: arm64
  • Host: Debian 13(ARM64 controller), Docker
  • OpenPLC Editor: OpenPLC.Editor-4.2.11.AppImage on Ubuntu (desktop, used only to compile/upload)

Docker run command:

docker run -d \
  --name openplc-runtime \
  --restart unless-stopped \
  --publish 8443:8443 \
  --publish 1502:502 \
  --publish 14840:4840 \
  --cap-add SYS_NICE \
  --cap-add SYS_RESOURCE \
  --cap-add IPC_LOCK \
  --ulimit memlock=-1:-1 \
  --memory 2g \
  --memory-swap 2g \
  --pids-limit 256 \
  --security-opt no-new-privileges:true \
  --log-driver json-file \
  --log-opt max-size=5m \
  --log-opt max-file=2 \
  --volume /mnt/data/openplc/runtime:/var/run/runtime \
  "$OPENPLC_IMAGE"

--cap-add IPC_LOCK and --ulimit memlock=-1:-1 were added specifically to try to fix this issue (see "What we already tried" below) — they fixed a separate mlockall failed: Cannot allocate memory error but did not fix the crash described here.

Summary

As soon as a compiled PLC program contains any Python-language Function Block (POU-level, created via Editor → "+" → Function Blocks → Language: Python), the PLC scan task (TASK0) crashes with signal 11 (SIGSEGV) on every single startup, immediately after the Python loader logs that it created a shared-memory segment. This happens even with a trivial Function Block with only 3 variables and no logic beyond a periodic file write — so it does not appear to depend on the number/size of VAR_INPUT/VAR_OUTPUT variables, or on the block's Python code.

Regular Python plugins (the plugin_driver.c/venv-based system used by modbus_slave, modbus_master, and opcua) load and run correctly in the same container — only the POU-level "Python Function Block" loader is affected.

Steps to reproduce

  1. In OpenPLC Editor v4.2.11, create a new Function Block, set Language to Python, with a minimal body, e.g.:
FUNCTION_BLOCK TEST_FILE_WRITE
  VAR_INPUT
    CPU_TEMPERATURE_C : REAL;
  END_VAR
  VAR_OUTPUT
    WRITE_COUNT : UDINT;
    LAST_ERROR : STRING;
  END_VAR

import time

TEST_FILE_PATH = "/var/run/runtime/test_write.txt"
TEST_INTERVAL_SECONDS = 10.0

_last_write_time = None

def block_init():
    global WRITE_COUNT, LAST_ERROR
    WRITE_COUNT = 0
    LAST_ERROR = ""

def block_loop():
    global _last_write_time, WRITE_COUNT, LAST_ERROR
    now = time.monotonic()
    if _last_write_time is not None and now - _last_write_time < TEST_INTERVAL_SECONDS:
        return
    _last_write_time = now
    try:
        with open(TEST_FILE_PATH, "a", encoding="utf-8") as f:
            f.write("%s CPU_TEMPERATURE_C=%s\n" % (time.strftime("%Y-%m-%d %H:%M:%S"), CPU_TEMPERATURE_C))
        WRITE_COUNT = WRITE_COUNT + 1
    except OSError as exc:
        LAST_ERROR = str(exc)
        print("TEST_FILE_WRITE: write failed: %s" % exc)

END_FUNCTION_BLOCK
  1. Declare and call one instance of it from main (any single scalar input is enough, e.g. a REAL from an existing struct field).
  2. Compile for OpenPLC Runtime v4, upload to the Runtime container above.
  3. Observe the Runtime log (docker logs openplc-runtime or the log file downloaded from the web UI).

Actual behavior

The PLC starts, then crashes on every single run, always at the same point:

[INFO]: PLC State: RUNNING
[INFO]: [Python loader] Random shared memory location: shmXXXXXX7Uyr5O
[ERROR]: [task TASK0] terminated by signal 11 — other tasks keep running

other tasks keep running — the OPC UA server, etc. come up fine afterward, so the crash is silent/non-fatal to the container as a whole, which makes it easy to miss: the Runtime looks "alive" (OPC UA responds) but the actual IEC/Python program logic is dead — no more scans execute, so no PLC logic runs and any Python FB's file writes never happen.

On stop, the Python loader cleanup log always shows the block with PID 0:

[INFO]: [Python loader] Cleaning up 1 Python function block(s)...
[INFO]: [Python loader] Stopping Python block: TEST_FILE_WRITE.py (PID 0)
[INFO]: [Python loader] Cleanup complete

PID 0 looks suspicious — possibly the subprocess spawn for the block never actually completed/registered a real PID before something else (the shared-memory setup?) segfaulted the parent task.

Expected behavior

The Python Function Block loads, block_init()/block_loop() run periodically as documented, and TASK0 keeps scanning normally.

What we already tried

  • Reduced the Function Block to the absolute minimum (3 variables, code above) — still crashes identically. Originally we hit this with a much larger custom Function Block (86 VAR_INPUT/VAR_OUTPUT STRING variables) and initially suspected a size/shared-memory-segment-size limit; splitting that block into smaller ones (down to ~50 variables) did not help, and this minimal 3-variable reproduction confirms size is not the deciding factor.
  • Multiple vs. single Python FB: tried with 1, 2, and 3 simultaneously-declared Python Function Blocks of varying sizes — all combinations crash the same way, always right after a "[Python loader] Random shared memory location" line. This happens even with just the single 3-variable block in the minimal repro above, so it's not specific to having more than one block. When 3 blocks were declared, the cleanup-on-stop log only ever listed 1 of them (Cleaning up 1 Python function block(s)...) even though the program declared 3 — but with only 1 block declared (the minimal repro), that same 1 block is listed in the cleanup log despite the crash still occurring. So the crash doesn't simply gate on "the Nth block fails to init" — some part of Python FB shared-memory setup crashes TASK0 regardless of block count, while whichever block(s) had already finished their own setup by that point still get tracked correctly.
  • mlockall failed: Cannot allocate memory was present in every log alongside the crash. Adding --cap-add IPC_LOCK and --ulimit memlock=-1:-1 to docker run (see command above) made this specific error disappear, but the signal 11 crash persisted identically — so this is a real but separate issue, not the root cause of the crash. (Possibly still worth a graceful-degradation fix on its own — the container previously failed mlockall on every start regardless of whether any Python FB was even used.)
  • sched_setscheduler failed: Operation not permitted is also present on every start (before and after the IPC_LOCK change) — this looks unrelated to the crash (it's a real-time scheduling priority request for the PLC thread, logged as non-fatal — "running default scheduling" — elsewhere in the log) but noting it in case it's relevant.
  • Confirmed the image is the latest available (v4.1.10, built 2026-08-11) — this is not a stale-build issue.

Log excerpt (single run, minimal repro)

[18-08-26 10:44:33] [ERROR]: mlockall failed: Cannot allocate memory
[18-08-26 10:44:33] [ERROR]: sched_setscheduler failed: Operation not permitted
[18-08-26 10:44:33] [INFO]: PLC State: RUNNING
[18-08-26 10:44:33] [INFO]: [Python loader] Random shared memory location: shmXXXXXXOHkDj2
[18-08-26 10:44:33] [ERROR]: [task TASK0] terminated by signal 11 — other tasks keep running

This excerpt predates the IPC_LOCK/memlock fix; a run after that fix shows the identical crash but without the mlockall line:

[18-08-26 10:58:53] [ERROR]: sched_setscheduler failed: Operation not permitted
[18-08-26 10:58:53] [INFO]: PLC State: RUNNING
[18-08-26 10:58:53] [INFO]: [Python loader] Random shared memory location: shmXXXXXX7Uyr5O
[18-08-26 10:58:53] [ERROR]: [task TASK0] terminated by signal 11 — other tasks keep running

Additional notes

  • Other Python-based plugins (modbus_slave, modbus_master, opcua — the venv-based plugin_driver.c system, distinct from the POU-level Python Function Block loader) load and run without any issue in the same container/build, so this appears isolated to python_loader.c / the POU Function Block shared-memory setup path specifically.
  • We searched the OpenPLC community forum and found a thread whose title exactly matches this crash message ("[task TASK0] terminated by signal 11 — other tasks keep running"), suggesting this isn't unique to our setup, but we weren't able to access its content.
  • Happy to provide the full log files, our Python FB source, or test further changes if it helps narrow this down — this is currently a hard blocker for using Python Function Blocks on (arm64) hardware.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions