0% found this document useful (0 votes)
32 views70 pages

SC Lab

lab record of secure coding

Uploaded by

imprince0905
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
32 views70 pages

SC Lab

lab record of secure coding

Uploaded by

imprince0905
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 70

School Of Computing

LAB RECORD

SECURE CODING

20CYS302

Amrita Vishwa Vidyapeetham Chennai


601 103, Tamil Nadu, India.

OCTOBER 2024

1|Page
BONAFIDE CERTIFICATE

University Register Number: CH.EN.U4CYS22066

This is to certify that this is a bonafide record


work done by Mr. Prince Tiwari

studying B.Tech Cyber Security

Internal Examiner 1 Internal Examiner 2

2|Page
INDEX
Programs:

Page
Index Topic & Description
Number
1 Locale Specific Behavior 4
2 Unspecified Behavior 6
3 Implementation Defined Behavior 8
4 Undefined Behavior 9
5 Bound ,Hi, Lo, Too_far, T_size, Null Terminated, Length 11
ARRO1-C. Do not apply the size of operator to a pointer when taking the size of an
6 13
array
7 STR30-C. Do not attempt the string literals 15
STR36-C. Do not specific the bound of character array initialized with a string
8 17
literal
9 MSC34-C.Do not use deprecated or obsolescent functions 19
10 Unbounded string copy 21
11 Off-by-one errors 23
12 Null termination error 25
13 String truncation error 28
14 Caller Allocates, Caller Frees 30
15 Callee Allocates, Caller Frees 32
16 Callee Allocates, Callee Frees 34
17 MEM09-C: Do not assume memory allocation functions initialize memory 36
18 MEM11-C: Do not assume infinite heap space 38
19 MEM32-C: Detect and handle memory allocation errors 41
20 EXP34-C: Do not dereference null pointer 42
21 CWE-416: Use After Free 44
22 CWE-415: Double Free 48
23 CWE-401: Missing release of memory after effective lifetime 52
24 MEM04-C. Do not perform zero-length allocation 54
25 Integer Wraparound 56
26 Integer overflow 58
Implicit (conversions is automatically done by compiler)
27 60
and Explicit (conversions occur by programmer)
Format Specifier
1. Direct user input in format string
28 61
2. Unrestricted string copy
3. Modify variadic function
Integer addition and subtraction
29 1. Pre-condition 64
2. Post-condition
30 Race Condition 68

3|Page
Ex.No : 1

Locale Specific Behavior

Aim:
The aim of the provided C code is to demonstrate the effect of changing the locale settings on the
output of the currency symbol in a program. It first sets the locale to Japanese (Japan) and retrieves
the currency symbol, then resets the locale to the user's default setting and retrieves the currency
symbol again.

Vulnerability:
Explanation: The program uses setlocale() to change the locale to Japanese and fetches the currency
symbol using localeconv(). It prints the Japanese currency symbol (¥) to the console.After resetting the
locale to the default, the program retrieves and prints the currency symbol again, which may vary
based on the user's environment.

Code:
#include <stdio.h>

#include <locale.h>

int main()

setlocale(LC_ALL,"ja_JP.utf8");

const struct lconv * currentlocale =localeconv();

printf("%s\n",currentlocale->currency_symbol);

setlocale(LC_ALL,"");

currentlocale=localeconv();

printf("%s\n",currentlocale->currency_symbol);

return 0;

Output:

4|Page
Mitigation:

Explanation: The if statements check if setlocale() is successful. If not, an error message is printed, and
the program exits with a non-zero status. This enhancement improves robustness by handling
potential locale setting failures, thus preventing undefined behavior in subsequent locale-dependent
operations.

#include <stdio.h>
#include <locale.h>

int main()
{

if (setlocale(LC_ALL, "ja_JP.utf8") == NULL) {


fprintf(stderr, "Error: Unable to set Japanese locale.\n");
return 1;
}

const struct lconv *currentlocale = localeconv();


printf("%s\n", currentlocale->currency_symbol);

if (setlocale(LC_ALL, "") == NULL) {


fprintf(stderr, "Error: Unable to reset locale to default.\n");
return 1;
}

currentlocale = localeconv();
printf("%s\n", currentlocale->currency_symbol);
return 0;
}

Output:

5|Page
EX.NO 2:

Unspecified Behavior

Aim:
The aim of the provided C code is to demonstrate the behavior of the post-increment operator (++)
when calculating the sum of two integers. The code increments the values of a and b after their
current values are used in the sum calculation.

Vulnerability:

Explanation: In the expression sum = a++ + b++, the current values of a (5) and b (7) are added
together before they are incremented. Therefore, sum is calculated as 5 + 7, resulting in 12. After the
operation, the values of a and b are incremented to 6 and 8, respectively, but this change does not
affect the computed sum.

#include <stdio.h>

int main()

int a=5, b=7;

int sum = a++ + b++;

printf("%d",sum);

return 0;

Output

6|Page
Mitigation:

Explanation: In the modified code, the sum is calculated directly from the original values of a and b
before any increments occur. This clarifies the sum calculation's intent. After computing the sum, the
increments are applied separately, making it clear that the result will always be the sum of the original
values, regardless of later changes to a and b. This helps prevent confusion regarding the order of
operations and the effects of the post-increment operator.

#include <stdio.h>

int main()

int a = 5, b = 7;

int sum = a + b;

a++;

b++;

printf("%d\n", sum);

return 0;

Output:

7|Page
EX.NO 3:

Implementation Defined Behavior

Aim: The aim of the provided C code is to demonstrate how to use the sizeof operator to determine
the size of an integer variable in bytes.

Vulnerability:

Explanation: The variable a is declared and initialized with the value 9. The line printf("size of int: %d",
sizeof(a)); uses the sizeof operator to get the size of the variable a, which is of type int. The sizeof
operator returns the size in bytes of the type of the operand, and in this case, it prints the size of an
integer, which is typically 4 bytes on most systems (though this may vary depending on the
architecture).

#include <stdio.h>

int main()

int a=9;

printf("size of int: %d",sizeof(a));

Output:

Mitigation:

Explanation: The modified code uses the format specifier %zu, which is appropriate for printing values
of type size_t, the type returned by sizeof. This ensures type safety and avoids potential warnings or
errors related to format mismatches. Adding bytes to the output string clarifies that the printed value
represents the size in bytes, making the output more informative and user-friendly.

8|Page
#include <stdio.h>

int main()

int a = 9;

printf("Size of int: %zu bytes\n", sizeof(a)); // Use %zu for size_t type

return 0;

Output:

EX.NO 4:

Undefined behavior

Aim: The aim of the provided C code is to demonstrate accessing an array element and the
consequences of trying to access an out-of-bounds index in an array.

Vulnerability:

Explanation: The array a is initialized with 5 elements, indexed from 0 to 4. The line printf("%d", a[5]);
attempts to access the element at index 5, which is out of bounds since valid indices for the array are 0
to 4. Accessing an out-of-bounds index leads to undefined behavior, which can cause the program to
crash, print garbage values, or produce unexpected results, depending on the compiler and runtime
environment.

#include <stdio.h>

int main()

int a[5]={1,2,3,4,5};

printf("%d",a[5]);

return 0;

9|Page
Output

Mitigation:

Explanation: The modified code introduces a variable index and checks if it is within the valid range (0
to 4). If the index is valid, it prints the corresponding element. If the index is out of bounds, an error
message is printed instead, preventing undefined behavior and improving the robustness of the
program by ensuring that invalid accesses are handled gracefully.

#include <stdio.h>
int main()
{
int a[5] = {1, 2, 3, 4, 5};
int index = 5; // Example index to access

if (index >= 0 && index < 5) {


printf("%d\n", a[index]);

} else {
printf("Error: Index %d is out of bounds!\n", index);
}

return 0;
}

Output:

10 | P a g e
Ex.No 5:

Bound ,Hi, Lo, Too_far, T_size, Null Terminated, Length

Aim: The aim of the provided C code is to demonstrate the declaration of a character array, output
various addresses of its elements, and explore the size of the array. It also attempts to access an out-
of-bounds index.

Vulnerability:

Explanation: The character array a is initialized with the string "Hello", which includes 6 characters (5
letters + 1 null terminator '\0'). The code prints:
 The "Bound of arr" as 5, which is a hardcoded value.
 The address of the last character (H) using &a[4].
 The address of the first character using &a[0].
 The size of the array using sizeof(a), which returns 6 (5 letters + 1 null terminator).
 The address of &a[5], which is technically out of bounds for the valid elements of the array but
is valid for the null terminator.

#include <stdio.h>

int main()

char a[] = "Hello";

printf("Bound of arr : %d\n",5);

printf("Hi of arr : %d\n",&a[4]);

printf("Lo of arr : %d\n",&a[0]);

printf("TargetSize of arr : %ld\n",sizeof(a));

printf("TooFar of arr : %d\n",&a[5]);

Output:

11 | P a g e
Mitigation:

Explanation: The output for "Bound of arr" now accurately reflects the number of characters in the
string (5) by using sizeof(a) - 1. The format specifier %p is used to print addresses instead of %d, which
is more appropriate for pointer types. Casting to (void*) is recommended when printing pointers to
avoid warnings or errors. The code checks if accessing &a[5] is valid before printing its address. Since
&a[5] points to the null terminator (which is valid), it is included in the output, but the check clarifies
the intention to avoid confusion.

#include <stdio.h>

int main()
{
char a[] = "Hello"; // Array size is 6 (5 letters + null terminator)
printf("Bound of arr : %d\n", sizeof(a) - 1); // Number of letters in the string
printf("Hi of arr : %p\n", (void*)&a[4]); // Use %p for pointer (address) output
printf("Lo of arr : %p\n", (void*)&a[0]);
printf("TargetSize of arr : %ld\n", sizeof(a)); // Size of the array in bytes

// Check if accessing a[5] is within bounds


if (sizeof(a) > 5) {
printf("TooFar of arr : %p\n", (void*)&a[5]); // Address of null terminator
} else {
printf("Error: Index 5 is out of bounds!\n");
}

return 0;
}

Output:

12 | P a g e
Ex.No 6:

ARRO1-C. Do not apply the size of operator to a pointer when taking the size of an array

Aim: The aim of the provided C code is to demonstrate how to calculate the size of an array in different
contexts, highlighting the difference between a vulnerable function that does not correctly determine
the array size and a safer function that takes the size as an argument.

Vulnerability:

Explanation: In the main function, an array a is initialized with 5 integers. The size of the array is
calculated using sizeof(a)/sizeof(a[0]), which gives the correct size (5) stored in s1. The arr_sizeVul
function attempts to calculate the size of the array using the same sizeof method, but this will not
work as intended because, in this function, the parameter arr[] decays to a pointer, and sizeof(arr)
returns the size of the pointer, not the size of the original array. The arr_size function correctly receives
the size of the array as an argument, ensuring it always has the correct size. The code prints the sizes
calculated from each method, demonstrating the issue with the vulnerable function.

#include <stdio.h>

int main(){

int s1,s2,s3, a[5] = {10,20,30,40,50};

s1 = sizeof(a)/sizeof(a[0]);

s2 = arr_sizeVul(a);

s3 = arr_size(a,s1);

printf("Size of arr is : %d\n",s1);

printf("Size of arr in the vulnerable function is : %d\n",s2);

printf("Size of arr in the non vulnerable function is : %d\n", s3);

int arr_sizeVul(int arr[]){

int s = sizeof(arr)/sizeof(arr[0]);

return s;

int arr_size(int arr[],int size){

int s = size;

return s;

13 | P a g e
Output:

Mitigation:

Explanation: Function Declarations: The function declarations for arr_sizeVul and arr_size are moved
above main to ensure proper visibility and avoid implicit declaration warnings. Comments: The code
now includes comments to clarify the intention of each part, explaining why one function is vulnerable
and the other is not. Return Statements: The logic remains the same, but the comments emphasize
the importance of understanding how array parameters decay into pointers and the implications for
size calculations.

#include <stdio.h>
// Function declarations
int arr_sizeVul(int arr[]);
int arr_size(int arr[], int size);

int main() {
int s1, s2, s3, a[5] = {10, 20, 30, 40, 50};

s1 = sizeof(a) / sizeof(a[0]); // Correct size calculation


s2 = arr_sizeVul(a); // Vulnerable size calculation
s3 = arr_size(a, s1); // Non-vulnerable size calculation

printf("Size of arr is : %d\n", s1);


printf("Size of arr in the vulnerable function is : %d\n", s2);
printf("Size of arr in the non-vulnerable function is : %d\n", s3);

return 0;
}

// Function to calculate size in a vulnerable way


int arr_sizeVul(int arr[]) {
// This will return the size of the pointer, not the array
int s = sizeof(arr) / sizeof(arr[0]); // Incorrect size calculation
return s;
}

// Function to safely return the size of the array


int arr_size(int arr[], int size) {
// Correctly uses the passed size
int s = size; // s will hold the correct size passed from main
return s;
}

14 | P a g e
Output

Ex.No 7 :

STR30-C. Do not attempt the string literals

Aim: The aim of the provided C code is to demonstrate the difference between modifying a string
stored in a character array (which is modifiable) and modifying a string literal (which is not modifiable
and leads to undefined behavior).

Vulnerability:

Explanation: The character array str2 is initialized with the string "string". The first character of str2 is
changed from 's' to 'S', and this modification is valid because str2 is an array, allowing for changes to its
contents. The modified string is printed successfully. In contrast, the pointer str1 is assigned the string
literal "string". String literals are stored in read-only memory, and attempting to modify them (like
str1[0] = 'S';) leads to undefined behavior. This can cause a segmentation fault or crash the program,
depending on the compiler and system.

#include <stdio.h>

int main(){

char str2[] = "string";

str2[0] = 'S';

printf("Non Vulnerable string array modification : %s\n",str2);

char *str1 = "string";

str1[0] = 'S';

printf("Vulnerable string literal modification : %s\n", str1);

Output:

15 | P a g e
Mitigation:

Explanation: Use of const: The pointer str1 is declared as const char *str1 = "string";. This indicates
that the content pointed to by str1 should not be modified, preventing accidental modifications.
Comment on Modification: The line attempting to modify str1 is commented out to avoid undefined
behavior and clarify that this operation should not be performed. This improves code safety and
emphasizes best practices when dealing with string literals. Clear Output Messages: The print
statements differentiate between the successful modification of the character array and the safe
handling of the string literal, enhancing the code's educational value.

#include <stdio.h>

int main() {

// Modifiable string in a character array

char str2[] = "string";

str2[0] = 'S'; // Valid modification

printf("Non Vulnerable string array modification: %s\n", str2);

// Attempting to modify a string literal

const char *str1 = "string"; // Use const to prevent modification

// str1[0] = 'S'; // Uncommenting this line would cause undefined behavior

printf("Vulnerable string literal (not modified): %s\n", str1);

return 0;

Output:

16 | P a g e
Ex.No 8:

STR36-C. Do not specific the bound of character array initialized with a string literal

Aim: The aim of the provided C code is to demonstrate the difference between initializing a character
array with a specified size and an unspecified size when dealing with string literals.

Vulnerability

Explanation:

String Initialization:

 The character array s1 is initialized with a specified size of 3 and contains the string "abc".
However, this initialization does not leave space for the null terminator ('\0'), which means s1
does not properly terminate as a string.

 The character array s2 is initialized without a specified size. This allows the compiler to
automatically determine the size, which includes the null terminator, so s2 properly contains
"abc\0".

Output:

 The printf function is used to print both strings. The output for s1 may produce undefined
behavior since it lacks a null terminator. Accessing it as a string could lead to unpredictable
results when the program attempts to find the end of the string.

 The output for s2 will correctly display abc, as it is properly null-terminated.

#include <stdio.h>

int main(){

const char s1[3] = "abc";

const char s2[] = "abc";

printf("string literal with bound : %s\n", s1);

printf("string literal without bound : %s\n", s2);

17 | P a g e
Output:

Mitigation

Explanation: Increased Size for s1: The size of s1 is changed to 4, allowing enough space for the
three characters of the string and the null terminator. This ensures that s1 is properly null-
terminated, preventing potential undefined behavior during output.

Clear Comments: Comments are added to explain the purpose of each initialization,
improving code readability and educational value.

Safe Outputs: Both s1 and s2 are printed safely, demonstrating correct string handling in C.

#include <stdio.h>

int main() {

const char s1[4] = "abc"; // Correctly allocate space for null terminator

const char s2[] = "abc"; // Automatically sized to include null terminator

printf("String literal with bound (s1): %s\n", s1); // Safe output

printf("String literal without bound (s2): %s\n", s2); // Safe output

return 0;

Output:

18 | P a g e
Ex.No 9

MSC34-C Do not use deprecated or obsolescent functions

Aim: The aim of the provided C code is to demonstrate the difference between using gets() and fgets()
for reading strings in C.

Vulnerability:

Explanation:

1. Using gets():

 The function gets(a) is used to read a string from standard input into the character array a.
However, gets() is unsafe and deprecated because it does not perform bounds checking, which
can lead to buffer overflows if the input exceeds the size of the array. Since a is defined with a
size of 3, entering more than 2 characters will overwrite adjacent memory.

 The program then prints the string read by gets(). If the input exceeds the allocated size, the
output may be unpredictable.

2. Using fgets():

 The function fgets(b, 3, stdin) is used to read up to 2 characters (leaving space for the null
terminator) from standard input into the character array b. This function is safer because it
limits the number of characters read, thus preventing buffer overflows.

 fgets() also retains the newline character in the input buffer if the user presses Enter before
filling the allocated space, which can affect the output.

#include <stdio.h>

int main(){

char a[3];

gets(a);

printf("Using gets : %s\n",a);

char b[3];

fgets(b,3,stdin);

printf("Using fgets : %s\n",b);

19 | P a g e
Output:

Mitigation

Explanation

1. Replacing gets(): The unsafe gets() function is replaced with fgets(), which is safer and allows
specifying the buffer size.

2. Dynamic Buffer Size: The code uses sizeof(a) and sizeof(b) to avoid hardcoding buffer sizes,
making it adaptable to future changes.
3. Handling Newline Character: After reading input with fgets(), the code checks for and removes
the newline character to ensure cleaner output.

4. User Prompts: Added prompts for user input clarify expectations and improve the user
experience.

#include <stdio.h>

#include <string.h>

int main() {

char a[3];

// Replace gets() with fgets() for safety

printf("Enter a string (max 2 characters): ");

fgets(a, sizeof(a), stdin); // Use sizeof to avoid magic numbers

printf("Using fgets (previously gets): %s\n", a);

char b[3];

printf("Enter another string (max 2 characters): ");

fgets(b, sizeof(b), stdin); // Read input safely

// Optionally remove the newline character if present

if (b[0] != '\0' && b[strlen(b) - 1] == '\n') {

b[strlen(b) - 1] = '\0'; // Remove newline character

20 | P a g e
}

printf("Using fgets: %s\n", b);

return 0;

Output:

Ex.no 10

Unbounded string copy


Aim: The aim of the provided C code is to demonstrate string copying using strcpy() and the potential
issues that arise when the source string exceeds the allocated size of the destination buffer.

Vulnerability

Explanation:

String Declaration:

 str is a character array of size 10, meaning it can hold up to 9 characters plus a null terminator
('\0').

 str2 is a pointer to the string "This String is longer than expected........", which is much longer
than 10 characters.

Using strcpy():

 The function strcpy(str, str2) copies the contents of str2 into str. However, since str2 is longer
than str, this operation causes a buffer overflow, meaning data is written past the bounds of
the str array. This can corrupt adjacent memory, lead to unpredictable program behavior,
crashes, or security vulnerabilities.

Output:

 The code prints the contents of str. However, due to the buffer overflow, the output may be
incorrect or the program could crash.

21 | P a g e
#include <stdio.h>

#include <string.h>

int main(){

char str[10];

char *str2="This String is longer than expected........";

strcpy(str,str2);

printf("%s\n",str);

return 0;

Output

Mitigation

Explanation:

Using strncpy(): The strncpy(str, str2, sizeof(str) - 1) function is used to copy only the first 9 characters
of str2 into str, leaving space for the null terminator. This avoids overflow.

Manual Null Termination: str[sizeof(str) - 1] = '\0'; ensures that str is always null-terminated, which is
essential for proper string handling in C.

Safety: This approach ensures that only the allowable number of characters are copied to the
destination buffer, avoiding buffer overflows.

22 | P a g e
#include <stdio.h>

#include <string.h>

int main() {

char str[10];

const char *str2 = "This String is longer than expected........";

// Use strncpy to copy only up to sizeof(str) - 1 characters

strncpy(str, str2, sizeof(str) - 1);

str[sizeof(str) - 1] = '\0'; // Ensure null termination

printf("Copied string: %s\n", str);

return 0;

Output

Ex.no 11

Off-by-one errors

Vulnerability:

Aim: The aim of the provided C code is to demonstrate iterating over an array and printing its
elements. However, the loop in the code contains an off-by-one error, which leads to accessing
memory outside the bounds of the array.

Explanation:

Array Declaration:

 numbers is an integer array of size 5, containing the values {1, 2, 3, 4, 5}.

Off-by-One Error:

 The for loop runs from i = 0 to i <= 5. Since arrays in C are zero-indexed, the valid indices for
the numbers array are 0 through 4. However, the condition i <= 5 causes the loop to access the
numbers[5] element, which is out of bounds, leading to undefined behavior. This could result
in printing garbage values, program crashes, or other unpredictable behavior.

23 | P a g e
#include <stdio.h>

int main() {

int numbers[5] = {1, 2, 3, 4, 5};

for (int i = 0; i <= 5; i++) {

printf("%d\n", numbers[i]);

return 0;

Output

Mitigation:

Explanation:

Loop Condition Correction: The condition in the for loop is changed to i < 5, ensuring that the loop
only iterates through valid indices (0 to 4), thus preventing the out-of-bounds access.

Safe Execution: The corrected loop prints the values of the array (1, 2, 3, 4, 5) safely without accessing
memory outside the array.

#include <stdio.h>

int main() {

int numbers[5] = {1, 2, 3, 4, 5};

// Corrected loop to avoid out-of-bounds access

for (int i = 0; i < 5; i++) {

printf("%d\n", numbers[i]);

return 0;

24 | P a g e
Output:

Ex.No 12

Null termination error

Aim: The aim of the provided C code is to concatenate two strings using strncat() and print the result.
However, the code contains a potential buffer overflow risk because the size of the destination buffer
dest is too small to hold the concatenated result.

Vulnerability

Explanation:

String Declaration:

 dest is a character array of size 10, initialized with "Hello". The array has 5 characters plus the
null terminator ('\0'), leaving only 4 bytes of space for further concatenation.

 src is a character array containing "World!", which is 6 characters long.

Using strncat():

 The strncat(dest, src, 6) function appends 6 characters from src to dest. However, dest only has
space for 4 more characters, including the null terminator. This leads to a buffer overflow, as
more characters than dest can hold are being written into it, which can result in undefined
behavior, crashes, or security vulnerabilities.

25 | P a g e
#include <stdio.h>

#include <string.h>

int main() {

char dest[10] = "Hello";

char src[] = "World!";

strncat(dest, src, 6);

printf("Concatenated string: %s\n", dest);

return 0;

Output

Mitigation

Explanation:

Size Limitation: The strncat() function is updated to use sizeof(dest) - strlen(dest) - 1, which ensures
that we concatenate only as many characters as can fit in the remaining space in dest, leaving room for
the null terminator.

 strlen(dest) calculates the current length of dest, and sizeof(dest) - strlen(dest) - 1 calculates
how much space remains in dest for the new string and the null terminator.

Safe Concatenation: This ensures no buffer overflow occurs, and the program operates safely.

26 | P a g e
#include <stdio.h>

#include <string.h>

int main() {

char dest[10] = "Hello";

char src[] = "World!";

// Ensure we only concatenate the number of characters that fit in the remaining space

strncat(dest, src, sizeof(dest) - strlen(dest) - 1);

printf("Concatenated string: %s\n", dest);

return 0;

Output

27 | P a g e
Ex.No 13

String truncation error

Aim: The aim of the provided C code is to safely copy a portion of a longer string (originalString) into a
smaller buffer (truncatedString) using strncpy(), ensuring that the copied string is properly null-
terminated to prevent buffer overflows.

Vulnerability:

Explanation:

String Declaration:

 originalString is a long string that exceeds the size of the target buffer truncatedString, which is
only 20 bytes in size.

 The goal is to safely copy part of originalString into truncatedString without causing a buffer
overflow.

Using strncpy():

 The strncpy(truncatedString, originalString, sizeof(truncatedString) - 1) function copies up to


sizeof(truncatedString) - 1 characters from originalString to truncatedString, leaving room for
the null terminator.

 After copying, the code explicitly sets truncatedString[sizeof(truncatedString) - 1] = '\0' to


ensure the destination string is null-terminated, preventing undefined behavior during output.

Output:

 The code prints the truncated version of originalString. Since the target buffer is smaller, the
string will be truncated after copying the first 19 characters.

#include <stdio.h>

#include <string.h>

int main() {

char originalString[] = "This is a long string that will be truncated";

char truncatedString[20];

strncpy(truncatedString, originalString, sizeof(truncatedString) - 1);

truncatedString[sizeof(truncatedString) - 1] = '\0';

printf("Truncated string: %s\n", truncatedString);

return 0;

28 | P a g e
Output

Mitigation:

Explained:

Size Limitations: The strncpy() function is used to copy only up to sizeof(truncatedString) - 1 characters,
preventing buffer overflow.

Null Termination: After copying, the program explicitly ensures that truncatedString is null-terminated,
which is crucial to avoid undefined behavior when the string is printed.

Truncated Output: The program prints the first 19 characters of originalString, followed by the null
terminator, safely handling the string's truncation.

#include <stdio.h>

#include <string.h>

int main() {

char originalString[] = "This is a long string that will be truncated";

char truncatedString[20];

// Safely copy part of the original string, leaving space for the null terminator

strncpy(truncatedString, originalString, sizeof(truncatedString) - 1);

// Ensure the string is null-terminated

truncatedString[sizeof(truncatedString) - 1] = '\0';

// Output the truncated string

printf("Truncated string: %s\n", truncatedString);

return 0;

29 | P a g e
Output

Ex.No 14

Caller Allocates, Caller Frees

Aim: The aim of this program is to demonstrate dynamic memory allocation in C using malloc() and
realloc(). The program allocates memory for an array, initializes and prints its values, resizes the array
using realloc(), and then deallocates the memory using free().

Explanation
#include <stdio.h>
#include <stdlib.h>
int main() {
int *arr;
int n = 5;
// Allocate memory for an array of 5 integers
arr = (int *)malloc(n * sizeof(int));
if (arr == NULL) {
printf("Memory allocation failed\n");
return 1; // Exit the program with an error code
}
// Initialize the array
for (int i = 0; i < n; i++) {
arr[i] = i + 1;
}
// Print the array elements
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");

30 | P a g e
// Change the size of the allocated memory
n = 10;
arr = (int *)realloc(arr, n * sizeof(int));
if (arr == NULL) {
printf("Memory reallocation failed\n");
return 1; // Exit the program with an error code
}
// Initialize the new part of the array
for (int i = 5; i < n; i++) {
arr[i] = i + 1;
}
// Print the updated array elements
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
// Deallocate the memory
free(arr);
return 0;
}
Output

Ex.No 15

31 | P a g e
Callee Allocates, Caller Frees

Aim: The aim of this program is to demonstrate how to use a function to dynamically allocate memory
for an array, return a pointer to that memory, and properly manage the memory lifecycle using
malloc() and free().

Explanation:

Function createArray():

 This function takes an integer size as a parameter and returns a pointer to an array of integers.

 Inside the function, memory is dynamically allocated using malloc():


int *arr = (int *)malloc(size * sizeof(int));

 A check is performed to ensure that memory allocation is successful. If malloc() returns NULL,
the program prints an error message and exits.

 The array is initialized with values from 1 to size using a loop:


arr[i] = i + 1;

 The function returns a pointer to the allocated memory (arr).

In the main() function:

 The integer size is set to 5, meaning the array will contain 5 elements.

 The function createArray(size) is called, and the pointer it returns is assigned to the variable
myArray.

 The elements of the array are printed using a loop.

Memory Deallocation (free):

 After using the dynamically allocated array, the memory is freed using the free() function:
free(myArray);

 This ensures there is no memory leak.

32 | P a g e
#include <stdio.h>

#include <stdlib.h>

// Function that allocates memory and returns a pointer to it

int* createArray(int size) {

int *arr = (int *)malloc(size * sizeof(int));

if (arr == NULL) {

printf("Memory allocation failed\n");

exit(1); // Exit the program if memory allocation fails

// Initialize the array

for (int i = 0; i < size; i++) {

arr[i] = i + 1;

return arr;

int main() {

int size = 5;

int *myArray;

// Call the function that allocates memory

myArray = createArray(size);

// Print the array elements

for (int i = 0; i < size; i++) {

printf("%d ", myArray[i]);

printf("\n");

33 | P a g e
// Free the allocated memory

free(myArray);

return 0;

Output:

Ex.No 16

Callee Allocates, Callee Frees

Aim: The aim of this program is to demonstrate how a function can internally handle dynamic memory
allocation, usage, and deallocation without returning any pointer, thereby abstracting memory
management details away from the main program.

Explanation

Function processArray():

 This function takes an integer size as a parameter to determine how many elements the array
will hold.

 Memory is dynamically allocated using malloc():


int *arr = (int *)malloc(size * sizeof(int));

 A check is performed to ensure that memory allocation is successful. If malloc() returns NULL,
the program prints an error message and the function exits early (return;).

Array Initialization:

 The array is initialized with values from 1 to size using a loop:


arr[i] = i + 1;

Processing the Array:

 In this case, processing involves printing the array elements. This is done using a loop that
prints each element of the array:
printf("%d ", arr[i]);

 The function prints the array elements after initialization.

34 | P a g e
Memory Deallocation:

 After the array is processed (printed), the memory allocated by malloc() is freed using free():
free(arr);

 This ensures proper memory management and prevents memory leaks.

In the main() function:

 The integer size is set to 5, meaning the array will contain 5 elements.

 The function processArray(size) is called, which handles the entire memory lifecycle
(allocation, processing, deallocation).

 No further memory management is required in main() because processArray() manages its


own memory.

#include <stdio.h>

#include <stdlib.h>

// Function that allocates, uses, and then frees memory internally

void processArray(int size) {

// Allocate memory for an array of integers

int *arr = (int *)malloc(size * sizeof(int));

if (arr == NULL) {

printf("Memory allocation failed\n");

return; // Exit function if memory allocation fails

// Initialize the array

for (int i = 0; i < size; i++) {

arr[i] = i + 1;

// Process the array (e.g., print the values)

printf("Array elements: ");

for (int i = 0; i < size; i++) {

printf("%d ", arr[i]);

printf("\n");

// Free the allocated memory

free(arr);

35 | P a g e
}

int main() {

int size = 5;

// Call the function that handles memory allocation and deallocation internally

processArray(size);

// No need to free memory here; it is handled within processArray

return 0;

Output:

Ex.No 17

MEM09-C: Do not assume memory allocation functions initialize memory

Aim: The aim of the provided C code is to demonstrate the difference between using malloc() (which
allocates uninitialized memory) and calloc() (which allocates and initializes memory to zero) when
dynamically allocating memory in C.

Vulnerability

Explanation:

1. Using malloc():

o The uninitialized_memory() function uses malloc() to allocate 10 bytes of memory for


buffer. However, malloc() does not initialize the memory, meaning it could contain
garbage values (leftover data in memory).

o The strcpy(buffer, "Hello") function is used to copy the string "Hello" into buffer. Since
"Hello" is only 6 bytes (5 characters + null terminator), it fits within the allocated
memory. However, the memory may contain uninitialized values beyond the copied
string, which can lead to undefined behavior if not handled carefully.

2. Using calloc():
o The initialized_memory() function uses calloc() to allocate 10 bytes of memory and
initialize it to zero. This ensures that all bytes in the buffer are initially set to 0.

36 | P a g e
o The same strcpy(buffer, "Hello") is used to copy the string, and the program prints the
initialized memory. This approach ensures predictable and safe behavior, as the
memory is already initialized to zero.

3. Output:

o The output will show the difference between using uninitialized and initialized
memory. While the output for both cases may appear correct, using malloc() could
lead to issues when the memory is accessed without initialization.

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

void uninitialized_memory() {

char *buffer = (char *)malloc(10);

strcpy(buffer, "Hello"); // Unsafe, buffer may contain garbage values

printf("Uninitialized memory: %s\n", buffer);

free(buffer);

void initialized_memory() {

char *buffer = (char *)calloc(10, sizeof(char)); // Safe, memory initialized to 0

strcpy(buffer, "Hello");

printf("Initialized memory: %s\n", buffer);

free(buffer);

int main() {

uninitialized_memory();

initialized_memory();

return 0;

Output

37 | P a g e
In above code both vulnerable and mitigation code is available as functions

Explanation:

Uses malloc(), but immediately calls memset(buffer, 0, 10) to initialize the allocated memory to zero
before using it. This prevents potential issues caused by uninitialized memory. Functions now include a
check to ensure that buffer is not NULL after memory allocation. This adds robustness by handling
memory allocation failures.

Ex.No 18

MEM11-C: Do not assume infinite heap space

Aim: The aim of the provided C code is to demonstrate the behavior of the malloc() function when
attempting to allocate an extremely large amount of memory, specifically the maximum value for a
size_t type, which is generally not feasible for allocation.

Vulnerability:

Explanation

Memory Allocation Attempt:

 The allocate_large_memory() function tries to allocate memory by setting size to (size_t)-1.


This value represents the maximum possible size for a size_t, which would typically result in an
attempt to allocate more memory than is available on the system.

 The call to malloc(size) attempts to allocate this large amount of memory, which is generally
impossible, and will likely fail.

Error Handling:

 After the malloc() call, the code checks whether buffer is NULL. If the allocation fails (which it
likely will in this case), it prints a message indicating that memory allocation failed. If it
somehow succeeded (which is highly unlikely), it would print a success message and free the
allocated memory.

38 | P a g e
#include <stdio.h>

#include <stdlib.h>

void allocate_large_memory() {

size_t size = (size_t)-1; // Attempting to allocate an extremely large amount of


memory

char *buffer = (char *)malloc(size);

if (buffer == NULL) {

printf("Memory allocation failed. Heap space is not infinite.\n");

} else {

printf("Memory allocation succeeded.\n");

free(buffer);

int main() {

allocate_large_memory();

return 0;

Output

Mitigation:

Explanation:

Size Limit Check: Before calling malloc(), the code now includes a check to see if size exceeds a
reasonable limit (in this case, 1 GB). This prevents attempts to allocate an unmanageably large amount
of memory. Output Improvement: If the size is too large, the program will inform the user that the
allocation is not attempted, avoiding unnecessary calls to malloc().

39 | P a g e
#include <stdio.h>

#include <stdlib.h>

void allocate_large_memory() {

size_t size = (size_t)-1; // Attempting to allocate an extremely large amount of memory

// Check for reasonable size limits

if (size > 1024 * 1024 * 1024) { // Example: Limit to 1 GB

printf("Requested size is too large. Allocation not attempted.\n");

return;

char *buffer = (char *)malloc(size);

if (buffer == NULL) {

printf("Memory allocation failed. Heap space is not infinite.\n");

} else {

printf("Memory allocation succeeded.\n");

free(buffer);

int main() {

allocate_large_memory();

return 0;

Output

40 | P a g e
Ex.No 19

MEM32-C: Detect and handle memory allocation errors

Aim: The aim of the provided C code is to demonstrate safe memory allocation using malloc() and
proper error handling. It allocates a small amount of memory and checks if the allocation was
successful.

Explanation:

Memory Allocation:

 The handle_memory_allocation() function attempts to allocate 100 bytes of memory using


malloc(size).

 size is set to 100, indicating that we want to allocate enough memory for 100 bytes (which is a
reasonable amount for many use cases).

Error Handling:

 The code checks if the result of malloc() (stored in buffer) is NULL. If it is NULL, this indicates
that memory allocation failed (e.g., if the system runs out of memory). In that case, the
program prints an error message and exits using exit(EXIT_FAILURE).

 If the memory allocation is successful, a success message is printed, and the allocated memory
is subsequently freed with free(buffer).

#include <stdio.h>

#include <stdlib.h>

void handle_memory_allocation() {

size_t size = 100;

char *buffer = (char *)malloc(size);

if (buffer == NULL) {

printf("Memory allocation failed. Exiting program.\n");

exit(EXIT_FAILURE);

printf("Memory allocation succeeded.\n");

free(buffer);

41 | P a g e
int main() {

handle_memory_allocation();

return 0;

Output

Ex.No 20

EXP34-C: Do not dereference null pointer

Aim: The aim of this guideline (EXP34-C) is to prevent dereferencing null pointers in C code.
Dereferencing a null pointer leads to undefined behavior, which can cause program crashes, data
corruption, and security vulnerabilities. The goal is to ensure that pointers are always valid before
being dereferenced.

Vulnerability

Explanation:
A null pointer is a pointer that does not point to any valid memory location. Dereferencing such a
pointer (i.e., accessing the memory it points to) results in undefined behavior, which can manifest as
segmentation faults or other runtime errors.

#include <stdio.h>

void process_data(int *data) {

// Vulnerable: dereferencing a null pointer

printf("Processing data: %d\n", *data); // May cause a crash if data is NULL

int main() {

int *ptr = NULL; // Initialize pointer to NULL

// Attempt to process data

42 | P a g e
process_data(ptr); // This will dereference a null pointer

return 0;

Output

Mitigation

Explanation:

To mitigate the risk of dereferencing null pointers, you should always check whether a pointer is NULL
before dereferencing it. This can be achieved by adding conditional checks to ensure that the pointer
points to valid memory before any dereferencing operation.

#include <stdio.h>

#include <stdlib.h>

void process_data(int *data) {

// Mitigated: check for null pointer before dereferencing

if (data == NULL) {

printf("Error: data pointer is NULL. Cannot process data.\n");

return; // Exit the function if the pointer is NULL

printf("Processing data: %d\n", *data); // Safe to dereference

int main() {

int *ptr = NULL; // Initialize pointer to NULL

// Attempt to process data

process_data(ptr); // Now safely checks for NULL before dereferencing

43 | P a g e
// Example of valid pointer usage

int value = 42;

ptr = &value; // Assign a valid address

process_data(ptr); // This will safely process the data

return 0;

Output

Ex.No 21

CWE-416: Use After Free

Aim

The aim of this code is to illustrate the concept of "use after free" (UAF), which occurs when a program
continues to use a pointer after the memory it points to has been freed. This can lead to undefined
behavior, security vulnerabilities, and program crashes. The goal is to show both a vulnerable example
and a safe usage pattern to mitigate this issue.

Vulnerability

Explanation

1. Use After Free:

o In the use_after_free() function, memory is allocated for a buffer, and then it is freed.
However, the code attempts to dereference the buffer pointer after it has been freed.
This is an example of UAF, which can lead to unpredictable behavior, including
program crashes.

2. Safe Memory Usage:

o In the safe_memory_usage() function, after freeing the memory, the pointer is set to
NULL. This prevents accidental dereferencing of a pointer that points to freed memory,
allowing for safer memory management.

44 | P a g e
#include <stdio.h>

#include <stdlib.h>

void use_after_free() {

char *buffer = (char *)malloc(20);

if (buffer == NULL) {

printf("Memory allocation failed.\n");

exit(EXIT_FAILURE);

free(buffer); // Unsafe usage after freeing the memory

// Undefined behavior: using buffer after it has been freed

printf("Use after free: %s\n", buffer);

void safe_memory_usage() {

char *buffer = (char *)malloc(20);

if (buffer == NULL) {

printf("Memory allocation failed.\n");

exit(EXIT_FAILURE);

free(buffer);

buffer = NULL; // Nullify the pointer after freeing

if (buffer != NULL) {

printf("Safe usage: %s\n", buffer);

} else {

printf("Pointer is null, safe to proceed.\n");

45 | P a g e
}

int main() {

use_after_free();

safe_memory_usage();

return 0;

Output

Mitigation

Explanation:

To mitigate the use-after-free vulnerability:

1. Nullify Pointers: After freeing memory, set the pointer to NULL. This way, any subsequent
dereference checks can safely identify that the pointer no longer points to valid memory.

2. Avoid Accessing Freed Memory: Ensure that no accesses to freed memory occur after calling
free().

#include <stdio.h>

#include <stdlib.h>

void use_after_free() {

char *buffer = (char *)malloc(20);

if (buffer == NULL) {

printf("Memory allocation failed.\n");

exit(EXIT_FAILURE);

free(buffer); // Unsafe usage after freeing the memory

46 | P a g e
// Undefined behavior: using buffer after it has been freed

printf("Use after free: %s\n", buffer);

void safe_memory_usage() {

char *buffer = (char *)malloc(20);

if (buffer == NULL) {

printf("Memory allocation failed.\n");

exit(EXIT_FAILURE);

free(buffer);

buffer = NULL; // Nullify the pointer after freeing

if (buffer != NULL) {

printf("Safe usage: %s\n", buffer);

} else {

printf("Pointer is null, safe to proceed.\n");

int main() {

use_after_free();

safe_memory_usage();

return 0;

Output

47 | P a g e
Ex. No 22

CWE-415: Double Free

Aim: The aim of this code is to demonstrate the "double free" vulnerability, where memory that has
already been freed is attempted to be freed again. This can lead to undefined behavior, which might
cause program crashes, memory corruption, or security vulnerabilities. The goal is to provide both a
vulnerable example and a safe pattern to avoid this issue.

Vulnerability

Explanation

1. Double Free:

o In the double_free() function, memory is allocated for a buffer and then freed.
However, the code attempts to free the same buffer again, resulting in a double free
vulnerability. This can corrupt the memory management data structures and lead to
unpredictable behavior.

2. Safe Memory Usage:

o In the safe_memory_usage() function, after freeing the memory, the pointer is set to
NULL. This prevents the risk of double freeing the memory, as subsequent checks for
validity (i.e., checking if the pointer is NULL) will not lead to an attempt to free freed
memory.

#include <stdio.h>

#include <stdlib.h>

void double_free() {

char *buffer = (char *)malloc(20);

if (buffer == NULL) {

printf("Memory allocation failed.\n");

exit(EXIT_FAILURE);

free(buffer);

free(buffer); // Double free, undefined behavior

48 | P a g e
void safe_memory_usage() {

char *buffer = (char *)malloc(20);

if (buffer == NULL) {

printf("Memory allocation failed.\n");

exit(EXIT_FAILURE);

free(buffer);

buffer = NULL; // Nullify the pointer after freeing

if (buffer != NULL) {

free(buffer); // Safe, but won't be called since buffer is NULL

} else {

printf("Pointer is null, safe to proceed.\n");

int main() {

double_free();

safe_memory_usage();

return 0;

Output

49 | P a g e
Mitigation

Explanation

To mitigate the double-free vulnerability:

1. Nullify Pointers After Freeing: After calling free(), set the pointer to NULL. This ensures that
any subsequent free calls on the same pointer can be safely ignored since freeing a NULL
pointer is a no-op in C.

2. Avoid Double Free: Implement checks to prevent multiple free calls on the same pointer.

#include <stdio.h>

#include <stdlib.h>

void double_free() {

char *buffer = (char *)malloc(20);

if (buffer == NULL) {

printf("Memory allocation failed.\n");

exit(EXIT_FAILURE);

free(buffer);

// buffer is set to NULL to prevent double free

buffer = NULL; // Nullify the pointer after freeing

// Attempt to free again, safely handled

if (buffer != NULL) {

free(buffer); // This won't execute since buffer is NULL

void safe_memory_usage() {

char *buffer = (char *)malloc(20);

if (buffer == NULL) {

printf("Memory allocation failed.\n");

50 | P a g e
exit(EXIT_FAILURE);

free(buffer);

buffer = NULL; // Nullify the pointer after freeing

if (buffer != NULL) {

free(buffer); // Safe, but won't be called since buffer is NULL

} else {

printf("Pointer is null, safe to proceed.\n");

int main() {

double_free();

safe_memory_usage();

return 0;

Output

51 | P a g e
Ex. No 23

CWE-401: Missing release of memory after effective lifetime

Aim: The aim of this code is to demonstrate the concept of a memory leak in C programming. A
memory leak occurs when a program allocates memory but fails to free it before the program
terminates, resulting in lost memory that cannot be reclaimed. The goal is to show a vulnerable
example of memory leak and a safe usage pattern to prevent it.

Vulnerability

Explanation

1. Memory Leak:

o In the memory_leak() function, memory is allocated for a buffer using malloc, but it is
never freed. This leads to a memory leak because the allocated memory remains
inaccessible and is not returned to the system.

2. Safe Memory Usage:

o In the safe_memory_usage() function, memory is allocated and then properly released


using free() after its usage. This prevents memory leaks and ensures efficient memory
management.

#include <stdio.h>

#include <stdlib.h>

void memory_leak() {

char *buffer = (char *)malloc(20);

if (buffer == NULL) {

printf("Memory allocation failed.\n");

exit(EXIT_FAILURE);

// Memory is allocated but not freed, causing a leak

printf("Memory allocated but not freed: %s\n", buffer);

void safe_memory_usage() {

52 | P a g e
char *buffer = (char *)malloc(20);

if (buffer == NULL) {

printf("Memory allocation failed.\n");

exit(EXIT_FAILURE);

printf("Memory allocated: %s\n", buffer);

free(buffer); // Properly releasing memory after its lifetime

int main() {

memory_leak();

safe_memory_usage();

return 0;

Output

Mitigation

Explanation

To mitigate memory leaks:

1. Free Allocated Memory: Always ensure that any memory allocated with malloc, calloc, or
realloc is freed using free() before the program terminates or the pointer is reassigned.

2. Nullify Pointers (Optional): After freeing memory, it is a good practice to set the pointer to
NULL to avoid accidental dereferencing of a freed pointer.

In the above code both vulnerable and mitigation function is there.

53 | P a g e
Ex. No 24

MEM04-C. Do not perform zero-length allocation

Aim: The aim of this code is to demonstrate the behavior and implications of allocating memory with a
size of zero in C. It also highlights the difference between zero-length allocations and valid memory
allocations, providing an example of safe memory management practices.

Explanation

1. Zero-Length Allocation:

o In the zero_length_allocation() function, memory is allocated with a size of zero using


malloc(). The behavior of allocating zero bytes can be implementation-defined. In
most implementations, this call will return a non-NULL pointer that should not be
dereferenced, as it doesn't point to a valid memory block.

2. Safe Allocation:

o In the safe_allocation() function, a proper allocation of 20 bytes is performed. This


example demonstrates a standard practice for allocating memory, checking for
allocation failure, and freeing the memory afterward.

#include <stdio.h>

#include <stdlib.h>

void zero_length_allocation() {

size_t size = 0;

char *buffer = (char *)malloc(size);

if (buffer == NULL) {

printf("Memory allocation failed.\n");

} else {

printf("Allocated zero-length buffer: %p\n", (void *)buffer);

free(buffer); // Freeing the allocated zero-length buffer

54 | P a g e
void safe_allocation() {

size_t size = 20;

char *buffer = (char *)malloc(size);

if (buffer == NULL) {

printf("Memory allocation failed.\n");

exit(EXIT_FAILURE);

} else {

printf("Allocated buffer of size %zu: %p\n", size, (void *)buffer);

free(buffer); // Properly freeing the buffer

int main() {

zero_length_allocation();

safe_allocation();

return 0;

Output

Note: Here also both vulnerable and mitigated code is there as functions

55 | P a g e
Ex. No 25

Integer Wraparound

Aim: The aim of this code is to demonstrate the concept of integer wraparound (or overflow)
in C programming when performing arithmetic operations with unsigned integers. It also
provides a safe way to handle potential overflow situations to prevent undefined behavior in
applications.

Explanation

1. Integer Wraparound:

o In the integer_wraparound() function, an unsigned integer a is assigned the


maximum value representable by an unsigned int (UINT_MAX). When 1 is
added to a, it wraps around to 0 due to the overflow, which is a common issue
in arithmetic operations.

2. Safe Integer Addition:

o In the safe_integer_addition() function, before performing the addition, the


code checks if adding b to a would cause an overflow. If the addition would
exceed the maximum value, a warning message is printed. Otherwise, it safely
performs the addition and displays the result.

#include <stdio.h>

#include <stdlib.h>

#include <limits.h>

void integer_wraparound() {

unsigned int a = UINT_MAX;

unsigned int b = 1;

unsigned int result = a + b; // Wraparound occurs here

printf("Result of wraparound: %u\n", result);

56 | P a g e
}

void safe_integer_addition() {

unsigned int a = UINT_MAX;

unsigned int b = 1;

if (UINT_MAX - a < b) {

printf("Integer overflow will occur, cannot add %u and %u\n", a, b);

} else {

unsigned int result = a + b;

printf("Safe addition result: %u\n", result);

int main() {

integer_wraparound();

safe_integer_addition();

return 0;

Output

Note: Here also both vulnerable and mitigated code is there as functions

57 | P a g e
Exp. No 26

Integer overflow

Aim: The aim of this code is to demonstrate the concept of integer overflow in C programming when
performing arithmetic operations with signed integers. It also provides a method to safely handle
potential overflow situations to prevent undefined behavior in applications.

Explanation

1. Integer Overflow:

o In the integer_overflow() function, an integer a is assigned the maximum value


representable by an integer (INT_MAX). When 1 is added to a, it results in overflow,
which leads to undefined behavior, causing the result to wrap around to a negative
value.

2. Safe Integer Addition:

o In the safe_integer_addition() function, before performing the addition, the code


checks if adding b to a would cause an overflow. If the addition would exceed the
maximum value, a warning message is printed. Otherwise, it safely performs the
addition and displays the result.

#include <stdio.h>

#include <stdlib.h>

#include <limits.h>

void integer_overflow() {

int a = INT_MAX;

int b = 1;

int result = a + b; // Overflow occurs here

printf("Result of overflow: %d\n", result);

void safe_integer_addition() {

int a = INT_MAX;

int b = 1;

58 | P a g e
if (a > INT_MAX - b) {

printf("Integer overflow will occur, cannot add %d and %d\n", a, b);

} else {

int result = a + b;

printf("Safe addition result: %d\n", result);

int main() {

integer_overflow();

safe_integer_addition();

return 0;

Output

Note: Here also both vulnerable and mitigated code is there as functions

59 | P a g e
Ex. No 27

Implicit (conversions is automatically done by compiler) and Explicit (conversions occur by


programmer)

Aim: The aim of this code is to demonstrate the concepts of implicit and explicit type conversions
(also known as type casting) in C programming. It shows how data types can be converted
automatically or manually during assignments and expressions.

Explanation

1. Implicit Conversion:

o In the implicit_conversion() function, an integer a is assigned the value 5. When


a is assigned to a double variable b, the conversion happens automatically
without any explicit action by the programmer. This is known as implicit
conversion or type coercion.

2. Explicit Conversion:

o In the explicit_conversion() function, a double a is assigned the value 5.7. To


convert a to an integer b, the programmer explicitly casts a to an integer type
using (int). This truncates the decimal part, demonstrating that explicit
conversions require the programmer's intervention.

#include <stdio.h>

void implicit_conversion() {

int a = 5;

double b = a; // Implicit conversion from int to double

printf("Implicit conversion: %d -> %f\n", a, b);

void explicit_conversion() {

double a = 5.7;

int b = (int)a; // Explicit conversion from double to int

printf("Explicit conversion: %f -> %d\n", a, b);

60 | P a g e
int main() {

implicit_conversion();

explicit_conversion();

return 0;

Output

28) Format Specifier

1. Direct user input in format string

2. Unrestricted string copy

3. Modify variadic function

Aim: The aim of this code is to demonstrate common vulnerabilities related to buffer overflows and
unsafe function usage in C programming, particularly in handling user input and string manipulation. It
illustrates potential pitfalls when using functions such as scanf, strcpy, and variadic functions with user-
controlled input.

Explanation

1. Direct User Input:

o The direct_user_input() function takes a string input from the user using scanf. If the
user enters a string longer than the buffer size (50 characters), this leads to a buffer
overflow, potentially overwriting adjacent memory and causing undefined behavior.

2. Unrestricted String Copy:

o The unrestricted_string_copy() function copies a long string from source to destination


without checking the size of the destination buffer. This can lead to buffer overflow, as
strcpy does not perform bounds checking.

3. Variadic Function Vulnerability:

61 | P a g e
o The modify_variadic_function() uses a format specifier with a user-controlled input. If
the format string contains malicious content, it can lead to vulnerabilities like format
string attacks.

4. Safe Variadic Function:

o The safe_variadic_function() shows a safer way to use variadic functions by avoiding


user-controlled format strings, thus preventing potential exploitation.

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include <stdarg.h>

void direct_user_input() {

char buffer[50];

printf("Enter a string: ");

scanf("%s", buffer); // Potential overflow if input exceeds buffer size

printf("You entered: %s\n", buffer);

void unrestricted_string_copy() {

char source[100] = "This is a long string that exceeds the buffer size.";

char destination[50];

strcpy(destination, source); // Unsafe copy, no bounds checking

printf("Copied string: %s\n", destination);

void modify_variadic_function(const char* format, ...) {

va_list args;

va_start(args, format);

vprintf(format, args); // Using format specifier with user-controlled input

va_end(args);

62 | P a g e
void safe_variadic_function(const char* format, const char* str) {

printf(format, str); // Safe usage by avoiding user-controlled format strings

int main() {

direct_user_input();

unrestricted_string_copy();

modify_variadic_function("%s\n", "This is a variadic function test.");

safe_variadic_function("%s\n", "This is a safe variadic function test.");

return 0;

Output

63 | P a g e
Ex. No 29

Aim:

The aim of integer addition and subtraction is to perform arithmetic operations on integer values while
ensuring correctness and safety, especially in the context of potential overflows or underflows.
Understanding the pre-conditions and post-conditions helps in validating the operations and ensuring
they behave as expected.

For Integer Addition and Subtraction, potential vulnerabilities arise when proper checks (pre-
conditions and post-conditions) are not enforced, leading to integer overflow or underflow issues

Integer addition and subtraction

1. Pre-condition

2. Post-condition

Integer Addition Overflow:

This occurs when the result of an addition exceeds the maximum limit of the data type.

#include <stdio.h>

#include <limits.h>

void vulnerable_addition() {

int x = INT_MAX;

int y = 10;

int result = x + y;

printf("Vulnerable addition result: %d\n", result);

int main() {

vulnerable_addition();

return 0;

64 | P a g e
Output

Mitigated Code (Addition with Pre-Condition Check):

#include <stdio.h>

#include <limits.h>

void mitigated_addition() {

int x = INT_MAX;

int y = 10;

// Pre-condition: Check for overflow

if (x > INT_MAX - y) {

printf("Addition overflow detected! Cannot perform addition.\n");

} else {

int result = x + y;

printf("Safe addition result: %d\n", result);

int main() {

mitigated_addition();

return 0;

Output

65 | P a g e
2) Integer Subtraction Underflow

This happens when the result of a subtraction goes below the minimum limit of the data type.

#include <stdio.h>

#include <limits.h>

void vulnerable_subtraction() {

int x = INT_MIN;

int y = 10;

int result = x - y; // This will cause an underflow

printf("Vulnerable subtraction result: %d\n", result);

int main() {

vulnerable_subtraction();

return 0;

Output

Mitigated Code (Subtraction with Pre-Condition Check):

#include <stdio.h>

#include <limits.h>

void mitigated_subtraction() {

int x = INT_MIN;

int y = 10;

// Pre-condition: Check for underflow

if (x < INT_MIN + y) {

66 | P a g e
printf("Subtraction underflow detected! Cannot perform subtraction.\n");

} else {

int result = x - y;

printf("Safe subtraction result: %d\n", result);

int main() {

mitigated_subtraction();

return 0;

Output

67 | P a g e
Ex. No 30

Race Condition

Aim: The aim of a race condition program is to demonstrate a situation in which two or more threads
or processes access shared resources concurrently, leading to unexpected or erroneous outcomes. The
program should highlight the potential issues that arise when proper synchronization mechanisms are
not employed to ensure safe access to shared resources.

Explanation:

A Race Condition vulnerability occurs when two or more threads or processes access shared resources
concurrently, and the behavior of the program depends on the timing of their execution. This can lead
to inconsistent or unexpected results.

1) Vulnerable Code (Race Condition):

In this example, two threads increment a shared counter simultaneously, leading to unpredictable
results.

#include <stdio.h>

#include <pthread.h>

int counter = 0;

void* increment_counter(void* arg) {

for (int i = 0; i < 1000000; i++) {

counter++;

return NULL;

int main() {

pthread_t t1, t2;

pthread_create(&t1, NULL, increment_counter, NULL);

pthread_create(&t2, NULL, increment_counter, NULL);

pthread_join(t1, NULL);

pthread_join(t2, NULL);

68 | P a g e
printf("Final counter value: %d\n", counter);

return 0;

2) Mitigated Code (Using Mutex for Synchronization):

A mutex (mutual exclusion) is used to ensure that only one thread can access the critical section
(incrementing the counter) at a time.

#include <stdio.h>

#include <pthread.h>

int counter = 0;

pthread_mutex_t lock;

void* increment_counter(void* arg) {

for (int i = 0; i < 1000000; i++) {

pthread_mutex_lock(&lock);

counter++;

pthread_mutex_unlock(&lock);

return NULL;

int main() {

pthread_t t1, t2;

pthread_mutex_init(&lock, NULL);

pthread_create(&t1, NULL, increment_counter, NULL);

pthread_create(&t2, NULL, increment_counter, NULL);

69 | P a g e
pthread_join(t1, NULL);

pthread_join(t2, NULL);

pthread_mutex_destroy(&lock);

printf("Final counter value: %d\n", counter);

return 0;

70 | P a g e

You might also like