Secure, Portable, and Ethical C EngineeringIntroductionC remains important in operating systems, embedded controllers, telecommunications, compilers, databases, safety-critical devices, and high-performance libraries. Its strengths—direct memory access, predictable execution, small runtime requirements, and close correspondence with hardware—also create significant engineering responsibilities. A pointer error can corrupt a process; an integer conversion can undermine a security boundary; an implementation-dependent assumption can make software fail on another processor; and an ethically careless design can expose users to surveillance, injury, or loss. Secure, portable, and ethical C engineering therefore treats correctness as more than producing output for ordinary inputs. It asks whether a program remains correct under hostile inputs, unusual machine representations, compiler optimisations, resource exhaustion, maintenance, and socially consequential use. These concerns are related but distinct. Security protects assets and behaviour against misuse. Portability preserves intended behaviour across conforming implementations and platforms. Ethics evaluates whether the system and its development practices respect people, law, safety, and professional duties. This chapter presents a disciplined approach to all three dimensions. It focuses on the C language and its standard library, while recognising that real assurance also depends on operating systems, compilers, build systems, hardware, testing, and organisational governance. Learning ObjectivesAfter studying this chapter, a learner should be able to: - Explain how undefined behaviour, implementation-defined behaviour, and unspecified behaviour affect security and portability.
- Identify common memory, integer, string, input-validation, concurrency, and resource-management vulnerabilities in C.
- Apply defensive design, secure coding, analysis, testing, and tool-assisted verification techniques.
- Design C interfaces that minimise portability assumptions and make ownership, bounds, errors, and invariants explicit.
- Evaluate ethical, legal, safety, privacy, and governance implications of low-level software.
- Analyse a realistic C engineering failure and propose proportionate controls.
1. A Unified Engineering ModelA useful model treats a C program as a transformation constrained by four contracts: - Language contract: the program obeys the C standard and does not rely on invalid operations.
- Platform contract: assumptions about widths, alignment, byte order, ABI, operating-system services, and compiler behaviour are explicit.
- Security contract: untrusted inputs cannot violate confidentiality, integrity, availability, or authorisation properties.
- Human and organisational contract: the system is developed and deployed responsibly, with traceability, accountability, accessibility, and respect for affected people.
A defect may violate more than one contract. For example, signed integer overflow is undefined behaviour under the C language rules. An optimiser may then remove a bounds check, creating a security vulnerability. The same code may also behave differently across compiler versions, harming portability. If it controls a medical actuator, the failure becomes an ethical and safety issue. Security should be considered in terms of assets, threats, trust boundaries, attack surfaces, and consequences. A threat model records what must be protected, who may act, what assumptions are made, and what happens if an assumption fails. Typical assets include credentials, memory contents, device state, personal data, and service availability. Portability is not merely “compiles on another machine.” It includes source portability, where the same source can be compiled; binary portability, where an executable or library ABI is compatible; behavioural portability, where observable behaviour is equivalent; and operational portability, where configuration, timing, limits, and deployment assumptions remain acceptable. Ethical engineering adds questions that technical tests alone cannot answer: Who benefits? Who bears risk? Is consent meaningful? Is data collection necessary? Can a user appeal an automated decision? Can operators understand and override a failure? A technically secure system can still be ethically unacceptable if it enables unjustified surveillance or discriminatory outcomes. 2. The C Behaviour Model and Its Security ConsequencesThe C standard distinguishes several forms of program behaviour. | Category | Meaning | Engineering implication |
|---|
| Defined behaviour | The standard specifies the result | Usually portable, subject to stated conditions | | Implementation-defined behaviour | The implementation chooses a documented result | Query or isolate the choice | | Unspecified behaviour | One of several permitted results may occur | Do not depend on a particular choice | | Undefined behaviour | The standard imposes no requirements | Prevent it; tools cannot make it safe | | Constraint violation | A required diagnostic applies, generally at translation | Correct the program before deployment |
Examples of undefined behaviour include accessing outside an array, dereferencing a null or invalid pointer, using a value of an uninitialised automatic object, shifting by an invalid count, and signed integer overflow. Undefined behaviour is not equivalent to “the program probably crashes.” The compiler may assume that it never occurs and transform surrounding code accordingly. Consider: #include <stddef.h>
#include <stdbool.h>
bool valid_index(int i, size_t n) {
return i >= 0 && i < (int)n;
}The cast from size_t to int may be implementation-defined or produce an unsuitable value when n is too large. A safer interface avoids incompatible signedness: #include <stddef.h>
#include <stdbool.h>
bool valid_index(size_t i, size_t n) {
return i < n;
}If negative indices are part of the external interface, validate the signed value before converting it, and ensure that the permitted range is representable in the destination type. A central rule is: a security check must itself be defined, range-safe, and evaluated before the dangerous operation. Compiler warnings, sanitizers, or defensive-looking conditions do not repair a program whose execution has already entered undefined behaviour. Invariants, Contracts, and Trust BoundariesAn invariant is a property that must remain true at a program point. Examples are “length never exceeds capacity,” “a file descriptor is either valid and owned or marked invalid,” and “a parsed command is drawn from an allow-list.” A contract specifies preconditions, postconditions, and ownership responsibilities. At a trust boundary, data changes from less trusted to more trusted context—for example, network bytes entering a parser, or a configuration file influencing privileged operations. Validation should occur at the boundary, but it should also be repeated when a value crosses into a component with different assumptions. Validation includes syntax, range, length, encoding, semantic relationships, and resource cost. 3. Memory Safety and Resource Safety3.1 Bounds and object lifetimesC arrays do not carry their lengths. A function receiving a pointer should normally receive a length or a sentinel contract, and the contract must be documented. Prefer interfaces such as: int copy_bytes(unsigned char *dst, size_t dst_cap,
const unsigned char *src, size_t src_len);The function must reject dst_cap < src_len, define whether overlapping objects are allowed, and specify whether null pointers are valid when the corresponding length is zero. memcpy requires that source and destination do not overlap; memmove is required when overlap is possible. The classic error in string handling is confusing storage capacity with current length. A destination of capacity cap can hold at most cap - 1 ordinary C-string characters plus the terminating null byte. A robust operation checks the source length, the destination capacity, and the result of every relevant operation. Functions such as strncpy do not necessarily terminate the destination and may perform unnecessary padding; they are not a general-purpose safe string-copy solution. Use explicit length-based representations for binary data. A byte sequence may contain zero bytes and therefore cannot safely be treated as a null-terminated string. Keep text encoding, byte count, and storage capacity conceptually separate. 3.2 Lifetime, ownership, and aliasingEvery allocation should have a clearly identified owner and release path. Useful ownership policies include “caller allocates and caller frees,” “callee allocates and caller frees,” or “borrowed pointer valid only during the call.” Ambiguous ownership causes leaks, double frees, and use-after-free defects. A pointer becomes invalid when its object’s lifetime ends. Returning the address of a local object, retaining a pointer to a reallocated buffer, or using a pointer after free violates lifetime rules. After releasing a resource, setting the owning handle to a neutral value can reduce accidental reuse, although it cannot repair aliases held elsewhere. C’s effective-type and aliasing rules also matter. Accessing an object through an incompatible pointer type can be undefined. For portable byte-level inspection, character types may inspect object representations, but converting arbitrary bytes into a typed object still raises alignment, representation, lifetime, and trap-representation concerns. Serialization should use explicit encoding and decoding rather than copying a structure directly to a file or network. 3.3 Resource exhaustionSecurity includes availability. Unbounded allocation, recursion, input length, decompression, logging, or file growth can allow denial of service. Resource limits should be explicit and measured in appropriate types. A parser should reject excessive nesting, token length, total input size, and computational work. Failure must be safe: partial state should be discarded or rolled back, and resources released on every path. A common pattern is a single cleanup path: int load_data(const char *path) {
int result = -1;
FILE *fp = NULL;
unsigned char *buf = NULL;
fp = fopen(path, "rb");
if (fp == NULL) goto cleanup;
buf = malloc(4096);
if (buf == NULL) goto cleanup;
/* Validate and process data; update result only on success. */
result = 0;
cleanup:
free(buf);
if (fp != NULL) fclose(fp);
return result;
}This is not automatically secure—the omitted processing still requires validation—but it makes release obligations visible. In larger systems, structured cleanup macros or ownership wrappers may improve consistency, provided they remain understandable and correctly scoped. 4. Integer Safety and Input ProcessingInteger defects often precede memory defects. Problems include signed overflow, truncation, sign conversion, multiplication overflow in allocation sizes, and incorrect interpretation of return values. Before computing an allocation for count elements of size size, check the multiplication: #include <stdbool.h>
#include <stddef.h>
bool size_product(size_t count, size_t size, size_t *out) {
if (out == NULL) return false;
if (size != 0 && count > SIZE_MAX / size) return false;
*out = count * size;
return true;
}The condition is valid for unsigned size_t, assuming SIZE_MAX is available through the appropriate header and the product is intended to fit in size_t. It prevents wraparound in the size calculation; it does not prove that the resulting allocation is operationally affordable. Use fixed-width types such as uint32_t only when an exact width is required and the implementation provides that type. Use size_t for object sizes and indices into objects. Use ptrdiff_t for pointer differences when appropriate. Do not assume that int is 32 bits or that a char is signed. The macros in <stdint.h> and <limits.h> allow limits to be inspected rather than guessed. Input conversion must detect errors. atoi cannot reliably report malformed input or range errors. Prefer strtol, strtoul, or carefully designed wrappers: set errno to zero, pass a non-null end pointer, ensure that at least one character was consumed, reject unexpected trailing characters, and check the returned range against the intended destination type. Numeric syntax, permitted sign, leading whitespace, and maximum digits should be defined by the application. Format strings deserve special attention. Never use untrusted input as the format string: printf(user_text); /* unsafe */
printf("%s", user_text); /* format is controlled */The second form still requires a length policy and suitable output channel, but it prevents format-string interpretation. 5. Concurrency, Cryptography, and Other Security BoundariesData races in C are not merely stale reads; conflicting unsynchronised accesses to the same object, where at least one is a write, can produce undefined behaviour. Use the C11 threads and atomics facilities where supported, or a well-defined platform synchronisation API. volatile does not provide mutual exclusion or atomicity; it is primarily relevant to special memory-mapped or signal-related situations, subject to strict rules. A secure concurrent design identifies shared state, protects each invariant with an appropriate lock or atomic protocol, and defines lock ordering to prevent deadlock. Thread cancellation, signal handlers, and callbacks introduce additional reentrancy concerns. Functions called from signal handlers are severely restricted; do not perform general allocation, formatted I/O, or locking there. Cryptography should almost never be implemented from primitives in application code. Use a reviewed library with a documented API, secure random-number source, key lifecycle, authenticated encryption where suitable, and constant-time operations where secret-dependent timing matters. Do not store passwords with encryption alone; use a password-hashing scheme designed for that purpose. Zeroising sensitive data is difficult because compilers may remove ordinary writes, and copies may exist in registers or buffers. It must be approached using platform- and library-specific facilities, with realistic threat assumptions. Operating-system boundaries require least privilege. Validate paths, avoid constructing shell commands from untrusted strings, use safer process APIs, set restrictive file permissions, and treat environment variables and inherited descriptors as inputs. Temporary files require secure creation primitives rather than predictable names. Error messages should support diagnosis without disclosing secrets, tokens, or unnecessary internal details. 6. Portability EngineeringPortable C begins with conformance rather than folklore. Compile with a selected language standard, enable strong warnings, and treat warnings as engineering findings. Isolate platform-specific code behind narrow interfaces. Use feature detection and configuration tests rather than guessing from processor names. Important portability dimensions include: | Concern | Unsafe assumption | Better practice |
|---|
| Integer size | int has a universal width | Use limits or exact-width types | | Byte order | Host order equals protocol order | Encode and decode explicitly | | Alignment | Any address may hold any type | Respect alignment and use memcpy where suitable | | Text | All systems use the same encoding | Define encoding and conversion policy | | Newlines | File representation is universal | Use text/binary modes and documented formats | | Structure layout | No padding exists | Serialize fields explicitly | | Time | Wall clock is monotonic | Use a monotonic clock for durations | char signedness | Plain char has fixed signedness | Use unsigned char or signed char intentionally |
A portable wire format specifies field order, widths, signedness, scaling, encoding, maximum sizes, and error handling. For example, a 32-bit unsigned integer can be encoded by deliberate shifts into four bytes, provided the value is known to fit and the protocol’s byte order is defined. Directly transmitting a structure is fragile because of padding, alignment, compiler layout, and representation differences. The preprocessor can support portability but can also obscure control flow and type checking. Prefer typed functions, static inline functions, enumerations, and configuration headers over complex macro logic. If a macro evaluates an argument multiple times, document or redesign it. Names beginning with an underscore followed by an uppercase letter, or two underscores, are reserved in many contexts; avoid reserved identifiers. Reproducible builds improve both portability and security. Record compiler versions, flags, dependencies, source revisions, generated files, and target configuration. Build in a controlled environment, compare artefacts where feasible, and sign release metadata. Portability testing should include different compilers, warning levels, architectures, endianness where relevant, sanitizers, and library implementations—not merely different operating-system labels. 7. Assurance: From Coding Rules to EvidenceNo single method proves that a C program is secure. Assurance is layered: - Requirements and threat modelling: define security properties, safety constraints, legal requirements, and misuse cases.
- Design review: document ownership, invariants, trust boundaries, failure modes, and privilege separation.
- Implementation discipline: use a restricted, justified subset of C; avoid dangerous interfaces; make assumptions explicit.
- Static analysis: compiler diagnostics, linters, data-flow analysis, taint analysis, and rule sets such as CERT C or MISRA C where appropriate.
- Dynamic analysis: AddressSanitizer, UndefinedBehaviorSanitizer, leak detection, race detection, fuzzing, and fault injection.
- Testing: unit, integration, property-based, boundary, negative, regression, and cross-platform tests.
- Independent review and release control: peer review, dependency review, reproducible builds, vulnerability response, and documented exceptions.
Sanitizers are powerful during testing but are not proof of absence of defects and may alter timing or memory layout. Fuzzing is effective when input generation reaches deep states, failures are reproducible, and the test harness checks semantic invariants rather than only crashes. Formal methods can provide stronger guarantees for selected modules, but their assumptions and coverage boundaries must be stated. A useful engineering metric is not a simplistic “number of bugs,” but evidence tied to risk: coverage of trust-boundary code, unresolved high-severity findings, time to remediate vulnerabilities, reproducibility of builds, and percentage of requirements with tests or proofs. Metrics can be gamed; they must support judgement rather than replace it. 8. Ethical, Legal, and Governance ResponsibilitiesProfessional responsibility begins with competence and honesty. Engineers should disclose significant limitations, avoid claiming that testing proves safety, preserve evidence, and escalate risks that decision-makers may not understand. A schedule or commercial objective does not justify concealing a known memory-safety defect in safety-critical or security-sensitive software. Privacy principles include purpose limitation, data minimisation, retention limits, access control, transparency, and user rights. C makes it easy to collect raw data efficiently; that is not a justification for collecting it. Design should minimise sensitive data, protect it in memory and transit, and securely delete it according to a realistic retention policy. Safety-critical systems require hazard analysis, independent verification, fail-safe or fail-operational decisions appropriate to the domain, traceability, controlled change, and qualified personnel. Security and safety sometimes conflict: aggressive termination may prevent exploitation but create an unsafe physical state. The correct response is domain analysis, not a universal rule. Accessibility and inclusion are also engineering concerns. A low-level component can affect whether assistive devices respond reliably, whether users with limited connectivity can access a service, or whether error messages are understandable. Procurement and supply-chain decisions should examine open-source obligations, vulnerability disclosure processes, provenance, and maintenance capacity. Relevant law varies by jurisdiction and sector. Examples include data-protection, product-safety, export-control, computer-misuse, accessibility, and intellectual-property obligations. Legal compliance is a baseline, not a complete ethical analysis. Organisations should maintain a vulnerability disclosure process, incident-response plan, access review, audit trail, and clear accountability for risk acceptance. 9. Illustrative Case: Vulnerable Embedded Update ServiceBackgroundConsider a fictional industrial sensor gateway written in C. It receives firmware-update packets over a network, stores them in a fixed buffer, verifies a signature, and reboots into the new image. The gateway controls sensors in remote facilities and may be unreachable for months. ProblemA review discovers that the packet length is read into a signed 16-bit integer and then converted to size_t. The copy routine checks the packet length against the received network frame but not against the destination buffer. The firmware header is copied directly into a C structure, assuming the compiler’s padding and host byte order. Update failure leaves the device with the previous image only if a particular cleanup path executes. Debug logs include device identifiers and fragments of update data. AnalysisThe signed conversion permits inconsistent interpretations of length. A large or negative value can bypass a check or become a very large unsigned size. The unchecked copy creates a memory-corruption opportunity. Direct structure copying harms interoperability and may cause different fields to be read on another compiler. The cleanup defect threatens availability, while logs create privacy and information-disclosure risks. Signature verification alone does not solve parsing, rollback, privilege, or denial-of-service problems. Approach and solutionThe team first writes a threat model and update-state machine. The parser reads fixed-width fields from bytes using explicit decoding, rejects lengths above a configured maximum, checks every arithmetic operation, and limits total update work. The update process writes to an inactive partition, verifies authenticity and version policy, flushes and validates the image, and changes the boot marker only after all checks succeed. A recovery image and watchdog policy provide a defined failure state. The update service runs with least privilege and accepts updates only through an authenticated channel. Sensitive logs are replaced with event codes and controlled metadata. The implementation uses a narrow ownership model, compiler warnings, static analysis, sanitizers in host tests, fuzzing of malformed packets, cross-compilation, and hardware-in-the-loop tests. Independent reviewers inspect cryptographic integration and safety implications. A documented disclosure and rollback procedure is added. OutcomeThe redesigned service rejects malformed and oversized packets, behaves consistently across tested architectures, preserves the previous image on failure, and produces less sensitive telemetry. The organisation also discovers that its original process had no owner for long-term vulnerability response; governance changes are therefore part of the technical solution. Learning pointsSecurity is a system property, not a single signature check. Portability requires explicit representation and interface contracts. Availability and privacy must be considered alongside memory safety. Finally, ethical responsibility includes maintaining a deployed device after release and communicating residual risk honestly. 10. Best-Practice ChecklistBefore release, an engineering team should be able to answer “yes” or provide a documented exception to the following questions: - Are all external inputs length-checked, range-checked, encoded, and bounded for resource cost?
- Are undefined operations excluded by design, analysis, and testing?
- Are allocation sizes checked for arithmetic overflow?
- Are ownership, lifetime, aliasing, and cleanup obligations explicit?
- Are error returns handled, and do failures leave a safe state?
- Are concurrency invariants protected by suitable synchronisation?
- Are cryptographic functions supplied by a reviewed library and used according to its contract?
- Are platform assumptions isolated, tested, and documented?
- Are serialised formats independent of structure padding and host byte order?
- Are builds reproducible enough to support investigation and trusted release?
- Have static analysis, sanitizers, fuzzing, boundary tests, and independent review been applied proportionately?
- Have privacy, safety, accessibility, legal, supply-chain, and misuse implications been assessed?
- Is there a post-release process for vulnerability reports, patches, rollback, and user communication?
Contemporary DevelopmentsMemory-safe languages are increasingly used for new components, while C remains necessary for existing kernels, hardware interfaces, and constrained systems. A pragmatic strategy is to place new high-risk parsing or policy code in a memory-safe language where feasible, expose narrow foreign-function interfaces, and isolate unavoidable C through strong contracts and testing. This is not a claim that another language automatically provides security; interface misuse and design flaws remain possible. Compiler hardening, control-flow integrity, fortified library interfaces, capability-oriented operating systems, static analysis enhanced by machine learning, and automated vulnerability discovery can improve assurance. AI-assisted code generation requires special caution: generated C may contain plausible but invalid assumptions, unsafe API use, licence issues, or untested error paths. Generated code should be treated as untrusted output subject to review, testing, provenance checks, and human accountability. The long-term direction is evidence-based engineering: explicit contracts, machine-checkable properties where practical, reproducible toolchains, software bills of materials, coordinated vulnerability disclosure, and a shift from patching individual bugs toward reducing entire classes of defects. Chapter SummarySecure, portable, and ethical C engineering combines language correctness, platform awareness, threat resistance, and professional responsibility. Undefined behaviour must be prevented because it can invalidate both security reasoning and portability. Memory safety requires explicit bounds, lifetime, ownership, aliasing, and cleanup contracts. Integer operations, input conversions, concurrency, cryptographic boundaries, operating-system interfaces, and resource limits require deliberate controls. Portability depends on avoiding unrecorded assumptions about widths, representations, alignment, byte order, text, structure layout, time, and compiler behaviour. Explicit serialisation, feature detection, isolated platform code, cross-platform testing, and reproducible builds are practical foundations. Assurance is layered through threat modelling, review, static and dynamic analysis, fuzzing, testing, and controlled release. Ethical engineering extends beyond compliance. Privacy, safety, accessibility, fairness, supply-chain responsibility, vulnerability response, and honest communication must influence requirements and deployment. The embedded update case demonstrates that secure design is a coordinated system and governance activity rather than a collection of isolated coding tricks. Key TermsUndefined behaviour: Program behaviour for which the C standard imposes no requirements. Implementation-defined behaviour: Behaviour selected and documented by a particular implementation. Unspecified behaviour: One of several permitted behaviours selected without a documentation obligation. Trust boundary: A point where data, privilege, or assumptions change between components. Invariant: A property that must remain true at a defined program point. Precondition: A requirement that must hold before an operation begins. Postcondition: A property guaranteed after an operation succeeds. Memory safety: Prevention of invalid bounds, lifetime, pointer, and object-access operations. Use-after-free: Access to an object after its lifetime has ended. Integer overflow: A calculation whose mathematical result cannot be represented by its type; its consequences depend on the type and operation. Portability: The ability to preserve intended source, binary, behavioural, or operational properties across environments. Serialization: Encoding structured data into a defined external representation. Least privilege: Granting a component only the authority necessary for its task. Fuzzing: Automated generation or mutation of inputs to discover failures and invariant violations. Sanitizer: Instrumentation that detects selected runtime defects during execution. Data race: Conflicting unsynchronised accesses to shared data in concurrent execution. Threat model: A structured description of assets, actors, attack surfaces, assumptions, and consequences. Reproducible build: A build process capable of producing verifiably equivalent artefacts from specified inputs. Vulnerability disclosure: The process for reporting, coordinating, fixing, and communicating security weaknesses. Review QuestionsShort Answer Questions- Distinguish undefined, implementation-defined, and unspecified behaviour in C.
- Why should a binary protocol not normally transmit a C structure directly?
- What ownership information should a pointer-based API document?
- Why is
volatile not a replacement for a mutex or atomic operation? - State two ethical responsibilities that continue after software deployment.
Long Answer Questions- Explain how integer conversion and allocation-size overflow can lead to memory-safety vulnerabilities. Include a suitable defensive design.
- Discuss a layered assurance strategy for a security-sensitive C component, including the role and limitations of static analysis, sanitizers, fuzzing, and review.
- Analyse the main dimensions of C portability and propose a testing and build strategy for a multi-architecture library.
- Explain how trust boundaries, least privilege, input validation, and failure handling interact in a network-facing C service.
- Discuss why privacy, safety, accessibility, and vulnerability response should be treated as engineering requirements rather than optional organisational concerns.
Analytical / Application Questions- A parser reads a 32-bit length, adds 12 for a header, allocates that amount, and copies the input. Identify at least four possible defects and design a corrected validation sequence.
- An embedded controller must choose between terminating immediately on a suspected attack and continuing in a degraded mode. Analyse the decision using security, safety, availability, and ethical criteria.
- A library works on one little-endian 64-bit machine but fails on a 32-bit big-endian target. Develop a diagnosis plan covering representation, alignment, integer widths, structure layout, build configuration, and tests.
Multiple Choice Questions with AnswersQuestion 1Which category of C behaviour gives the implementation freedom to choose and document the result? A. Defined behaviour B. Implementation-defined behaviour C. Undefined behaviour D. Constraint violation Correct Answer: B. Implementation-defined behaviour Question 2Which type is generally appropriate for representing the size of an object or array? A. size_t B. float C. signed char D. time_t Correct Answer: A. size_t Question 3What is the essential precondition for using memcpy rather than memmove? A. The destination must be larger than the source B. The objects must not overlap C. The source must be a string D. Both pointers must be null when the length is zero Correct Answer: B. The objects must not overlap Question 4Which expression prevents unsigned size multiplication from wrapping when count and size are size_t values? A. count > SIZE_MAX / size when size is nonzero B. count > SIZE_MAX + size C. count < SIZE_MAX / size D. size > count * SIZE_MAX Correct Answer: A. count > SIZE_MAX / size when size is nonzero Question 5Why is directly serialising a C structure non-portable? A. Structures cannot contain integers B. Structure padding, alignment, and representation may differ C. The C standard forbids writing files D. Pointers are always required in structures Correct Answer: B. Structure padding, alignment, and representation may differ Question 6Which statement about volatile is correct? A. It guarantees mutual exclusion B. It guarantees atomic access to every object C. It is a general replacement for thread synchronisation D. It does not by itself provide mutual exclusion or atomicity Correct Answer: D. It does not by itself provide mutual exclusion or atomicity Question 7Which use of printf is the safer format-string pattern? A. printf(user_text); B. printf("%s", user_text); C. printf("%n", user_text); D. printf((char *)user_text); Correct Answer: B. printf("%s", user_text); Question 8What is the primary purpose of a trust boundary in threat modelling? A. To identify where data or privilege changes assumptions B. To guarantee that all data is encrypted C. To replace the need for testing D. To identify only physical network cables Correct Answer: A. To identify where data or privilege changes assumptions Question 9Which practice best supports behavioural portability for a network protocol? A. Copying host structures directly to the network B. Assuming native byte order at both endpoints C. Defining field widths, byte order, encoding, and limits explicitly D. Using implementation-specific padding as a delimiter Correct Answer: C. Defining field widths, byte order, encoding, and limits explicitly Question 10Why should AI-generated C code receive normal or stronger review? A. Generated code cannot compile B. AI output may contain plausible unsafe assumptions and untested paths C. C code needs no testing if generated automatically D. AI automatically proves absence of undefined behaviour Correct Answer: B. AI output may contain plausible unsafe assumptions and untested paths
|