Tracing Indirect Addressing Writes to a Shared DB in STEP 7

David Krause23 min read
HMI ProgrammingSiemensTroubleshooting
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

Problem Statement: Shared DB Values That Refuse to Stay

A STEP 7 user program with several hundred function blocks, instance DBs, and a handful of shared (global) DBs holds the recipe, setpoints, and mode bits that the rest of the machine reads. From time to time one particular shared DB refuses to hold whatever an engineer writes into it: the value stays correct for roughly one second and is then overwritten by something the engineer cannot identify. STEP 7's cross-reference (Querverweis / Reference Data) lists every direct symbolic access but reports nothing that touches the byte in question, even after refreshing the reference data and rebuilding the S7 program.

The behavior is the textbook signature of an indirect write. An indirect address in STEP 7 is built dynamically at runtime by an arithmetic instruction, by loading a pointer into one of the address registers (AR1, AR2), or by passing a pointer through an FB/FC parameter. Because the operand is computed at execution time, the offline cross-reference cannot know which DB, byte, or bit is actually being touched and silently excludes it from the symbol table. The same blind spot hides any data block opened with OPN DB [AR1,P#0.0] or AUF DB [AR2,P#0.0], any mass copy with SFC20 BLKMOV, any write issued by an HMI tag, any put/write issued by a peer CPU on PROFIBUS or PROFINET, and any broadcast executed through a Global Data (GD) circle.

This reference walks through every category of indirect write that touches an S7-300 / S7-400 shared DB, lists the instruction mnemonics and SFCs/SFBs that produce them, and gives a deterministic diagnostic workflow that closes the search without opening two hundred blocks by hand. The workflow is valid for STEP 7 V5.5, V5.6, and V5.7 (the latest classic STEP 7 release) on S7-300 (CPU 31x, CPU 319) and S7-400 (CPU 41x, CPU 416, CPU 417). TIA Portal equivalents are called out where the menu paths or operands differ.

Why the Cross-Reference Cannot See Indirect Writes

The Reference Data editor in STEP 7 V5.x is generated offline at compile time. It scans the STL or LAD/FBD source for explicit operands such as DB100.DBX0.0, L "speed_setpoint", or U M 12.3. Whenever the operand contains a pointer or an address register, the tool cannot resolve the concrete destination and drops the entry silently.

The following constructs, all common in machine control code, are invisible to the offline cross-reference:

  • Memory-indirect, register-indirect, and area-crossing STL commands.
  • Mass data movement with SFC20 BLKMOV, SFC21 FILL, SFC75 SET_MASK / SET / RESET, and SFC81 UBLKMOV.
  • Read/write SFCs that take an ANY pointer computed in the program (SFC15 WRITE, SFC22 CREAT_DB, the S7-communication SFBs).
  • S7 communication partners writing the DB over the backplane, PROFIBUS-DP, or PROFINET IO using PUT / WRITE SFBs on a peer CPU.
  • GD (Global Data) circles configured in NetPro.
  • HMI variables bound to the same DB through WinCC flexible, ProTool, TIA Portal HMI, or WinCC V7.
  • OPC DA / OPC UA servers (Simatic Net, Softing, Kepware) with the S7-300/400 as their source.
  • S7-PDIAG, S7-GRAPH, S7-HiGraph, and S7-SCL blocks that internally generate indirect access.
  • Diagnostic and block-status SFCs that write into the diagnostic buffer of the same DB if the project maps the buffer to a user DB.

Each category must be hunted separately. The workflow below gives the same execution order a maintenance engineer should follow: register-level STL, source grep, communication partners, system functions, HMI, GD, and finally the diagnostic buffer.

Indirect Addressing Modes in STEP 7 STL

STEP 7 STL supports three classes of indirect addressing on the S7-300 / S7-400. Every one of them resolves the destination at runtime and therefore defeats the offline cross-reference. The S7-1200/S7-1500 use the same syntax in STL but the operand field rules differ; TIA Portal hides STL behind the compiler so indirect writes are seen as %DB*.%DBW* expressions in the SCL source.

Class Syntax example Meaning Cross-ref visible?
Memory-indirect, area-internal A DBX[MD10] Byte/bit offset inside current DB computed from MD10 No
Memory-indirect, area-crossing A DBX[MD10] after OPN DB [AR1,P#0.0] Address register points to DB number, MD10 is offset No
Register-indirect, area-internal A DBX[AR1,P#0.0] AR1 holds offset inside current DB No
Register-indirect, area-crossing A D[AR1,P#10.0] AR1 is 32-bit pointer: bits 0..23 = byte/bit offset, bits 24..31 = area ID (B/M/D/T/Z) No
Parameter type (POINTER, ANY in FC/FB; VARIANT in SCL) // POINTER param Pointer passed into block Only the parameter signature, never the resolved DB

The area-crossing format is the most dangerous. The destination pointer is loaded into one of the address registers, and the instruction itself does not name the DB. The compiler and the cross-reference treat such an instruction as operand-less because the operand is built inside the register.

Typical STL that hides a write:

// Open DB by number loaded from somewhere dynamic
      L     #i_db_no                  // INT, e.g. from recipe block
      T     LW    0
      OPN   DB [LW 0]                 // DB number comes from a variable

// Build destination pointer in AR1
      L     #i_offset                 // DINT offset inside DB
      SLD   3                         // multiply by 8 to make bit offset
      L     P#DBX 0.0                 // area ID 0x84 = DB byte
      OD
      LAR1

// Write the value
      L     #i_value
      T     DBB [AR1,P#0.0]

The cross-reference will see OPN DB [LW 0] as a "DB operand without symbol" line and will not connect it to the user-defined shared DB that contains the variable the engineer is trying to protect. The T DBB [AR1,P#0.0] line is reported as touching an unknown memory area and is also dropped from the useful output.

Common indirect write idioms in SCL:

// SCL example 1: array element write
FOR i := 1 TO 100 DO
  "dbRecipe".array[i] := #src_value;
END_FOR;

// SCL example 2: AT view over a larger array
FUNCTION_BLOCK FB_Touch
VAR
  arr   : ARRAY[0..99] OF BYTE;
  view  : AT arr : ARRAY[0..99] OF BOOL;  // bit view
END_VAR
view[#index] := #state;

// SCL example 3: PEEK/POKE word addressing
FUNCTION_WORD := PEEK(area := 16#84, dbNo := #dbNo, byteOffset := #off);
POKE(area := 16#84, dbNo := #dbNo, byteOffset := #off, value := #v);

Right-click the SCL block in SIMATIC Manager and choose Generate STL source. The exported STL contains the inline access patterns LAR1, +AR1, T DBB[AR1,P#0.0], and UC SFC 20 that the grep step below will find.

Diagnostic Step 1: Online "Go to Location" With Overlapping Access

STEP 7 has a built-in trick that the offline cross-reference cannot replicate: the online "Go to Location" function works on the live CPU, jumps to the call site that last touched the address, and follows an indirect access as long as Overlapping access to memory areas is enabled. This is the fastest single-step diagnostic if the engineer can get the project online and force the offending write to repeat.

  1. Open the program editor (STL or LAD/FBD) for the block that contains the address of interest.
  2. Select Options > Customize > STL (or LAD/FBD) tab and tick Overlapping access to memory areas. Equivalent path in STEP 7 V5.7: Options > Settings > PC Internal > STL editor > Operand field overlap. In TIA Portal the corresponding setting is Tools > Options > PLC programming > STL > Operand field overlap.
  3. Place the cursor on the suspected operand (e.g. DB100.DBX4.0). Right-click and choose Go to > Location (in the German UI: Gehe zu > Verwendungsstelle).
  4. STEP 7 jumps to the next network that touches the address. For indirect accesses the dialog reports the address where the operand could be in the byte/bit overlap window. Confirm the open network and repeat from the new location.
  5. If the cursor lands on OPN DB [...] or on a register-indirect instruction, the tool can still resolve the symbolic location if the pointer source has been monitored online. Open a Monitor/Modify table on AR1 / AR2 and step the program in single-scan mode (PLC > Operating Mode > Single Scan) to catch the value at the moment of the write.
  6. Repeat for every overlapping operand the editor proposes. The byte/bit overlap window can offer four or five possible destinations; cross-reference the destinations against the variable that is being overwritten to narrow the search.

Limitations: "Go to Location" follows one step at a time. It cannot trace the entire chain that leads to AR1 being loaded. For multi-hop indirect chains it must be combined with a breakpoint on the instruction that writes the address register (see Step 6).

Note on operands: the byte/bit overlap window is limited to the operand types of the address being searched. A search on DB100.DBX4.0 can overlap with DB100.DBB4, DB100.DBW4, and DB100.DBD4, but not with DB100.DBB3. Set the search to DBW first to widen the overlap to byte and word matches.

Diagnostic Step 2: Export Sources and Grep the STL

When the project is offline only, or when there are dozens of blocks to scan, exporting the entire program to one or more STL source files and searching them with a plain text editor is the most reliable technique. A single STL file condenses every FB, FC, OB, DB, and UDT into one searchable document, and the indirect-addressing mnemonics are rare enough that a few grep patterns finish the search in seconds.

  1. In the SIMATIC Manager right-click the Blocks container and choose Generate Source. Pick a name such as ALL_BLOCKS.src. Accept the default option to generate all blocks.
  2. STEP 7 compiles the source. Re-edit any blocks that fail to compile because they contain SCL or GRAPH code; the export will skip them with a warning. For SCL blocks, right-click the block and choose Generate source > STL source to obtain a view of the indirect accesses the SCL compiler introduced.
  3. Open the resulting .src file in Notepad++, UltraEdit, VS Code, or less. Save a copy with CR/LF stripped so that grep -n matches work cleanly across line continuations.
  4. Run a battery of pattern searches against the file. Use case-insensitive matching and exclude comment lines with grep -vE '^\s*//'.

Recommended search patterns (run each one separately):

grep -niE 'OPN[[:space:]]+DB[[:space:]]*\['                       all_blocks.src
grep -niE 'AUF[[:space:]]+DB[[:space:]]*\['                       all_blocks.src   # German UI
grep -niE 'LAR[12]|TAR[12]|\+AR[12]|-AR[12]'                      all_blocks.src
grep -niE 'DBB?[[:space:]]*\[|DBW?[[:space:]]*\[|DBD?[[:space:]]*\[' all_blocks.src
grep -niE 'BLKMOV|FILL|UBLKMOV'                                    all_blocks.src
grep -niE 'SFC[[:space:]]*(15|20|21|22|23|75|81)\b'               all_blocks.src
grep -niE 'SFB?[[:space:]]*(8|9|12|13|14|15|16|18|19)\b'          all_blocks.src
grep -niE 'CALL[[:space:]]+"?(PUT|GET|USEND|URCV|BSEND|BRCV)\b'   all_blocks.src
grep -niE 'P#\s*DB[XBWD]|P#\s*D\s+B\s*\d'                          all_blocks.src
grep -niE '\bPEEK\b|\bPOKE\b'                                      all_blocks.src   # SCL peek/poke
grep -niE 'CREAT_DB|DEL_DB'                                        all_blocks.src
grep -niE 'DB_ANY|ANY_POINTER|POINTER'                             all_blocks.src
grep -niE 'TCI[A-Z]?|DPRD|DPWR'                                    all_blocks.src   # DP read/write

Each match line lists the block name and the network number. Open that block in the editor and inspect the surrounding network. If the offset inside the pointer matches the byte/bit that the shared DB is being written to, you have found the writer. Cross-check the network against the call stack captured in single-scan mode (Step 1) to confirm.

Tip: SCL code compiles into STL with intermediate temporary variables named t#db_xyz, #temp_dword, and #_index_. Any t#db_xyz := value statement where the left-hand side is computed from an index variable, an AT view on a larger variable, or a slice expression ("db"."struct"."arr"[i].field) is an indirect write to a shared DB. Repeat the grep on the generated STL by right-clicking the SCL block and choosing Generate STL source.

Diagnostic Step 3: PUT / GET, USEND / URCV, and S7 Communication

If the shared DB is part of a multi-CPU setup, an S7-1500/1200 peer, or a third-party controller talking to the S7-300/400 over S7 communication, the writer is most likely an external station that calls PUT (write to this CPU) or GET (read from this CPU). Neither call shows up in the user program of the target CPU because the write is performed by the operating system of the communication processor (CP) or the PN interface once the call has been authenticated.

  1. Open NetPro. Inspect every S7 connection terminating on the CPU. Note the connection partner, the connection ID, and the configured Active connection establishment flag.
  2. For each connection, open the partner's Properties and check whether the partner's Object list for PUT contains the affected DB number. In NetPro right-click the connection, select Object list, and see exactly which DBs are reachable. Restrict the list to the DBs the partner actually needs.
  3. On the receiving CPU, monitor the operating mode. A diagnostic buffer entry STOP due to communication error combined with event ID 0x13xx indicates a malformed PUT request. Event ID 0x35xx signals an unauthorized write attempt that was rejected by the protection level.
  4. If the program includes calls to SFB14 GET, SFB15 PUT, SFB8 USEND, SFB9 URCV, SFB12 BSEND, or SFB13 BRCV in OB1 or OB35, the receiving side of these blocks also issues a write to the SDO/SDB area when the data block pointer argument names the shared DB. Add those SFBs to the grep list of Step 2.
  5. For PROFINET IO / PROFIBUS-DP slaves that act as IO controllers (ET 200S with PN interface, SINAMICS drives with PN, ET 200pro), inspect the slave's slot configuration for any acyclic record writes. The CPU logs acyclic writes through the diagnostic buffer with event IDs 0x0E01 and 0x0E02.
  6. For S7 routing through a CP, open the CP's online diagnostics (right-click the CP in HW Config > Online > Diagnostics) and check the connection list. Routed connections can write to the CPU without appearing in the local connection table.
Security: PUT access from a partner is gated by the Protection level configured in HW Config (CPU Properties > Protection). If the protection is set to Complete protection the operating system rejects any PUT, but older firmware versions accepted PUT to the first few hundred bytes regardless. Update the CPU to the latest firmware for the family in question; see the STEP 7 V5.7 installation and updates page for firmware bundles and the SIMATIC S7-300/S7-400 firmware update entry for CPU firmware downloads.

Diagnostic Step 4: SFC20 BLKMOV, SFC21 FILL, and System Functions

SFC20 BLKMOV and SFC21 FILL copy or fill a region described by an ANY pointer. Because the source or destination ANY is computed at runtime from variables, the cross-reference reports the call as "block without parameter connection" and never connects it to the destination DB.

  1. Grep for CALL "BLKMOV", CALL SFC 20, or the SFC20 token (already covered by the pattern above).
  2. For each match, read the source and destination ANY arguments. The ANY pointer layout in STEP 7 is 10 bytes:
+------+------+------+------+------+------+------+------+------+------+
| 10h  | 00h  | typ  | rep. | byte/bit offset  (32-bit pointer)    |
+------+------+------+------+------+------+------+------+------+------+
   byte 0  byte 1  byte 2  byte 3  byte 4  byte 5  byte 6  byte 7  byte 8  byte 9

The data-type byte (byte 2) determines the operand: B#16#10 = DB byte, B#16#12 = DB word, B#16#14 = DB double word, B#16#20 = bit (Boolean), B#16#04 = word outside DB. Bytes 4-5 are the repetition count (length in bits for bit-typed ANY, in units of the type for byte/word/double word). Bytes 6-9 are the byte/bit offset within the operand area.

  1. Run the program with a watch on the ANY pointer just before the call. STEP 7 can display the byte/bit representation of the ANY: open the variable in the Monitor/Modify table as ABY (any binary) and check the data-type nibble. Confirm the byte 2 is 0x10..0x14, the length matches the variable the engineer is protecting, and the offset matches the byte/bit range being overwritten.
  2. Other SFCs that hide writes:
  • SFC15 WRITE — writes a row of data to a peer DP slave. Watch for an indirect destination pointer.
  • SFC75 SET_MASK / SET / RESET / RESET_BF — operate on a single bit or on a bit array of an input/output/process-image area. If the pointer points at the process-image output of a module that is also being read by the program, the value can flip.
  • SFC81 UBLKMOV — unaligned block move with the same ANY-pointer trap.
  • SFC22 CREAT_DB and SFC23 DEL_DB — dynamic DB creation. The created DB can be at the same number as the shared DB you are protecting, depending on the maximum DB number configured in HW Config.
  • SFC43 RE_TRIGR and SFC46 STP — STOP/RUN transitions can also be triggered by communication, but the symptom here is value-flip, not mode change.
  • SFC51 RDSYSST — reads a system state list. The output ANY can be redirected to a user DB and overwrite its first N bytes if the length is wrong.
Watch-out: Dynamic DB creation with SFC22 can steal the DB number of a shared DB if the project's Maximum DB number is greater than the highest numbered shared DB and the runtime permits overlapping numbering. Symptom: the program that "used to work" now gets writes from a newly created DB because both share the same slot in the DB register. Open HW Config, set Maximum DB number = (highest numbered shared DB) and reload the CPU.

Diagnostic Step 5: HMI, WinCC, ProTool, and OPC Tag Bindings

HMI software writes to DBs through the S7 communication channel the same way a peer CPU does, but the tag definition lives in a separate project. The HMI is therefore invisible to the STEP 7 cross-reference by design. Identify candidates as follows:

  1. List every HMI panel, WinCC station, OPC server, and S7-PM project that points to the same CPU. The fastest check is to open NetPro and look at the connections terminating on the CPU; HMIs appear with connection type S7-HMI.
  2. For each HMI project, open the tag editor and filter on the affected DB number (e.g. DB100). Tags whose name ends with _SP, _CMD, _W, or _M are typically writeable. Note the tag names and the polling cycle.
  3. In the HMI screens, search for the tag name. Almost every writable tag is connected to an Output field, a Slider, a Button, or a Recipe view. Open the screen and check the event configuration: a value entered in an I/O field is written immediately on change, while a slider writes on release. The one-second interval in the original symptom is consistent with a recipe list that re-applies its value on every cycle, with a PLC job from WinCC flexible's PlcJob, or with a comfort panel's 1-second cyclic write of a status tag that the HMI project bound to a writeable address by mistake.
  4. For WinCC V7, the relevant project is the Tag Management > SIMATIC S7 PROTOCOL SUITE > TCP/IP or Industrial Ethernet connection. Right-click the connection and choose Tags; filter on the DB number.
  5. For TIA Portal HMI tags (Comfort Panels, WinCC Unified), the tag configuration is inside the TIA Portal project itself and the relationship between the HMI tag and the DB is visible from the Connections view. Add the project to the TIA Portal multi-user server to inspect the binding.
  6. OPC DA and OPC UA servers (Simatic Net, Softing, Kepware) hold the configuration in a separate file. Disable the OPC server temporarily; if the DB value stabilises, the OPC tag is the writer. Replace the tag with a read-only tag or restrict the OPC group to read access.
  7. For ProTool / WinCC flexible projects, the connection configuration lives in Project > Connections. The tag list under Tags shows the DB address and access mode (read-only / read-write).
Tip: WinCC flexible and TIA Portal HMI both support an Operator authorization on every writable tag. Setting the tag's authorization to a high-level group that the operator's role does not have blocks the write at the panel, but does not stop scripts from writing. Audit scripts that call SetTag, SetTagRaw, SetTagBit, or SetProperty in the VBScript or C action sections.

Diagnostic Step 6: Global Data, CPs, and the Diagnostic Buffer

After all of the above have been checked, the residual cases are listed below. Each one is easy to miss because it does not produce a write in the user program.

  • Global Data (GD) circles configured with the CPU as receiver. Each GD row in NetPro contains the source byte/bit and the destination byte/bit. Open NetPro, double-click the GD table, and inspect every row whose destination is the shared DB in question. Disable the GD row temporarily by removing the receiver CPU from the circle; if the value now holds, the GD row was the writer.
  • CP routing through a CP (CP 343-1, CP 443-1 Advanced, CP 343-1 Lean). Routing partners can write into the CPU via the CP without a NetPro-visible S7 connection. Check the CP's connection list by opening CP Diagnostics in the online view.
  • System function error callbacks that write the error code into a user DB. SFC36 / SFC37 (mask / unmask events), SFC39 / SFC40 (OB priority), and SFC41 / SFC42 (delay / interrupt) all write into OB100 / OB121 / OB122 if those OBs are not loaded. Verify all OBs referenced by the program are loaded on the CPU.
  • Diagnostic buffer entries that reveal a cyclic write. Open PLC > Diagnostics/Setting > Diagnostic Buffer and look for repeated entries with the same time stamp pattern. Event IDs of interest:
Event ID Meaning Action
0x13xx Malformed PUT request from partner Restrict the object list on the partner's connection
0x35xx Unauthorized write attempt rejected by protection level Confirm CPU protection level; identify the source from the partner's IP
0x39xx Module / submodule failed Check the slot for a removed module that the program still references
0x4300 I/O access error when reading Indirect read of a non-existent slot — set a breakpoint
0x4543 STOP because of programming error (OB not loaded) Load the missing OB and re-test
0x0E01 / 0x0E02 Acyclic record write/read on a PN/DP slave Inspect the slave's slot configuration
0x38xx Communication partner disconnected Identify the partner and check whether its connection is needed

If the diagnostic buffer is empty and the value still flips, set a breakpoint on every indirect instruction in the suspect block. In the editor, right-click the STL line and choose Set Breakpoint. Force the CPU into Single Scan mode (PLC > Operating Mode > Single Scan) and step through the OB1 cycle. The breakpoint halts execution on the indirect write, and the call stack in the online view shows the path from the OB that triggered it.

For long-running cycles, set the breakpoint to Trigger on condition with a comparison on the value being overwritten. For example: DB100.DBW 4 <> 0. The CPU only halts when the condition becomes true, eliminating the need for visual inspection of the value during the single-scan.

Search Pattern Reference Table and Verification Procedure

Combine the steps above with the patterns in the table below. Each pattern points at a category of writer and lists the block type to inspect once the match is found.

Pattern (regex / token) Writer category Block to inspect
OPN DB [, AUF DB [ Indirect DB open inside FB/FC All FBs/FCs in the S7 program
LAR1, LAR2, +AR1, TAR1 Address-register based access Any block that handles arrays / loops
DBB[AR, DBW[AR, DBD[AR Register-indirect DB access Any block that handles array element write
CALL "BLKMOV", CALL SFC 20 Mass copy Any block that initialises or refreshes a structure
CALL "FILL", CALL SFC 21 Mass fill Block that zeroes or initialises the DB at startup
CALL "PUT", SFB15, FB15 Peer CPU writing this CPU NetPro object list, peer CPU's blocks
CALL "GET", SFB14, FB14 Peer CPU reading this CPU (response is local) Peer CPU's blocks, but verify DB has not been written via PUT side
CALL "USEND" / URCV / BSEND / BRCV S7 communication send/receive Block that performs acyclic or cyclic send
P#DBX, P#DBB in code (not in FB header) Static ANY that has been built dynamically Block where ANY is built from input parameters
PEEK, POKE in SCL SCL peek/poke word-addressed access SCL source of the block (right-click > STL source)
HMI tag linked to the DB HMI / WinCC / OPC write HMI project or OPC configuration
GD row destination = DB byte Global Data NetPro > GD table
SFC22 CREAT_DB with dynamic number Dynamic DB overlaps the static shared DB OB100 / OB1 startup blocks
SFC15 WRITE with dynamic ANY DP/PN acyclic record write Block that issues the write
TCI (TCON / TDISCON / TSEND / TRCV) Open user communication over TCP Block that handles the TCP send/receive

Verification procedure: once a candidate writer has been identified, force it offline and observe the shared DB. Concretely:

  1. In NetPro, temporarily delete the GD row (or comment it out) and download the project. If the DB value holds, GD is the writer.
  2. If the suspect is an HMI tag, set the tag's access to read-only in the HMI project and download to the panel. If the DB value holds, the HMI tag was the writer.
  3. If the suspect is an SFC call inside the CPU program, replace the SFC with NOP 0 and download. If the DB value holds, the SFC was the writer.
  4. For peer CPU PUT, disconnect the network cable or block the connection ID in NetPro (set connection status to disabled, then download). If the DB value holds, the peer is the writer.
  5. For OPC, stop the OPC server service. If the DB value holds, the OPC tag was the writer. Tighten the OPC group to read access.
  6. For SFC22 dynamic DB creation, temporarily set Maximum DB number in HW Config to the highest numbered shared DB and reload. If the DB value holds, the dynamic DB was overlapping.

Restore the configuration once the writer is documented and a controlled write path is in place. Add a comment in the shared DB header that lists every indirect writer and the protective pattern used to defend the value. Update the project's Symbol Table and Reference Data comment column with a one-line reference to the documentation block (e.g. // see FB123 "TouchGuard" for write protection).

Preventive pattern for the protected variable:

// FB "TouchGuard" — denies indirect writes to a single DB byte
FUNCTION_BLOCK FB_TouchGuard
VAR
    i_db   : INT;       // protected DB number
    i_byte : INT;       // protected byte offset
    i_value: BYTE;      // last allowed value (set from HMI once)
END_VAR
BEGIN
    // Read the protected byte once per cycle
    IF (#i_db > 0) AND (#i_byte > 0) THEN
        // Open the DB and read; if different from last allowed value, restore.
        ...
    END_IF;
END_FUNCTION_BLOCK

Pair this with a read-only protection level on the HMI tag and a restricted object list on every PUT connection. The combination closes the indirect-write loop in three independent layers.

Frequently Asked Questions

Why does my offline cross-reference (Querverweis) show nothing writing to a DB byte even though the value keeps changing online?

The cross-reference is computed at compile time from explicit operands in the source. STL constructs that resolve the destination at runtime — OPN DB [...], LAR1/LAR2, DBB[AR1,P#0.0], CALL SFC20 BLKMOV, peer PUT requests, HMI tag writes, and GD rows — do not appear in the offline output. Use the online "Go to Location" function with overlapping access enabled, or grep the generated STL source for the patterns listed above.

What is the difference between memory-indirect and register-indirect addressing in STEP 7 STL?

Memory-indirect uses a double word in the bit-addressed memory (M, D, L) as the offset or pointer. Register-indirect uses address register AR1 or AR2. Area-internal variants stay inside the same operand area (DB or M). Area-crossing variants carry both the area ID and the byte offset in the register and can switch between DB/M/I/Q in a single instruction. Only the area-crossing register-indirect form can address any DB byte from any block without re-opening the DB.

How can I prevent an external partner CPU from overwriting my shared DB through PUT?

Open the S7 connection in NetPro, clear the Object list for PUT entries that point at the protected DB, and set the CPU's Protection level to Write-protection or Write/read-protection with password. Also confirm the CP or PN interface firmware is current so that the PUT-rejection patch is active. Event ID 0x35xx in the diagnostic buffer confirms that an unauthorized PUT was rejected.

Can an HMI panel write to a shared DB through tags that the program does not name explicitly?

Yes. WinCC flexible, TIA Portal HMI, WinCC V7, and OPC DA/UA servers all bind to DB bytes by address and write whenever the operator changes a value or a script forces an update. Disable the HMI or OPC server, or set the tag access to read-only in the HMI project; if the value now holds, the HMI was the writer. Restrict write access through the panel's user administration to prevent the same issue from recurring.

Which STEP 7 diagnostic tool tells me whether SFC20 BLKMOV is the source of the overwrite?

Grep the generated STL source for CALL "BLKMOV", set a breakpoint on the BLKMOV call, open a Monitor/Modify table on the destination ANY pointer, and step the OB1 cycle. If the breakpoint halts on every write of the protected byte, BLKMOV is the writer. Replace the BLKMOV with a guarded copy that only runs when an enable bit is set, or refactor the call into a fixed-offset assignment.

Back to blog