diff --git a/lib/evmone/baseline.cpp b/lib/evmone/baseline.cpp
index 7a9caab651..61c9b013b5 100644
--- a/lib/evmone/baseline.cpp
+++ b/lib/evmone/baseline.cpp
@@ -69,7 +69,7 @@ CodeAnalysis analyze_legacy(bytes_view code)
 
 CodeAnalysis analyze_eof1(bytes_view container)
 {
-    const auto header = read_valid_eof1_header(container);
+    auto header = read_valid_eof1_header(container);
 
     // Extract all code sections as single buffer reference.
     // TODO: It would be much easier if header had code_sections_offset and data_section_offset
@@ -79,17 +79,7 @@ CodeAnalysis analyze_eof1(bytes_view container)
     const auto executable_code =
         container.substr(code_sections_offset, code_sections_end - code_sections_offset);
 
-    // Code section offsets relative to the beginning of the first code section.
-    // Execution starts at position 0 (first code section).
-    // The implementation of CALLF uses these offsets.
-    CodeAnalysis::CodeOffsets relative_offsets;
-    relative_offsets.reserve(header.code_offsets.size());
-    for (const auto offset : header.code_offsets)
-        relative_offsets.push_back(offset - code_sections_offset);
-
-    // FIXME: Better way of getting EOF version.
-    const auto eof_version = container[2];
-    return CodeAnalysis{executable_code, {}, eof_version, relative_offsets};
+    return CodeAnalysis{executable_code, std::move(header)};
 }
 }  // namespace
 
@@ -338,7 +328,7 @@ evmc_result execute(const VM& vm, ExecutionState& state, const CodeAnalysis& ana
 
     const auto code = analysis.executable_code;
 
-    const auto& cost_table = get_baseline_cost_table(state.rev, analysis.eof_version);
+    const auto& cost_table = get_baseline_cost_table(state.rev, analysis.eof_header.version);
 
     auto* tracer = vm.get_tracer();
     if (INTX_UNLIKELY(tracer != nullptr))
diff --git a/lib/evmone/baseline.hpp b/lib/evmone/baseline.hpp
index 1519534bc7..718e24d188 100644
--- a/lib/evmone/baseline.hpp
+++ b/lib/evmone/baseline.hpp
@@ -3,6 +3,7 @@
 // SPDX-License-Identifier: Apache-2.0
 #pragma once
 
+#include "eof.hpp"
 #include <evmc/evmc.h>
 #include <evmc/utils.h>
 #include <memory>
@@ -22,14 +23,10 @@ class CodeAnalysis
 {
 public:
     using JumpdestMap = std::vector<bool>;
-    using CodeOffsets = std::vector<uint16_t>;
 
     bytes_view executable_code;  ///< Executable code section.
     JumpdestMap jumpdest_map;    ///< Map of valid jump destinations.
-    uint8_t eof_version = 0;     ///< The EOF version, 0 means legacy code.
-    /// Offset of each code section relative to the beginning of the first code
-    /// section. We flatten the sections for cheap execution.
-    CodeOffsets code_offsets;
+    EOF1Header eof_header;       ///< The EOF header.
 
 private:
     /// Padded code for faster legacy code execution.
@@ -43,11 +40,8 @@ class CodeAnalysis
         m_padded_code{std::move(padded_code)}
     {}
 
-    CodeAnalysis(bytes_view code, JumpdestMap map, uint8_t version, CodeOffsets offsets)
-      : executable_code{code},
-        jumpdest_map{std::move(map)},
-        eof_version{version},
-        code_offsets{std::move(offsets)}
+    CodeAnalysis(bytes_view code, EOF1Header header)
+      : executable_code{code}, eof_header{std::move(header)}
     {}
 };
 static_assert(std::is_move_constructible_v<CodeAnalysis>);
diff --git a/lib/evmone/eof.cpp b/lib/evmone/eof.cpp
index d6e8aa87db..95e6e68659 100644
--- a/lib/evmone/eof.cpp
+++ b/lib/evmone/eof.cpp
@@ -430,7 +430,7 @@ std::variant<EOF1Header, EOFValidationError> validate_eof1(
         offset += code_size;
     }
 
-    EOF1Header header{code_sizes, code_offsets, data_size, types};
+    EOF1Header header{container[2], code_sizes, code_offsets, data_size, types};
 
     for (size_t code_idx = 0; code_idx < header.code_sizes.size(); ++code_idx)
     {
@@ -489,6 +489,8 @@ EOF1Header read_valid_eof1_header(bytes_view container)
 
     EOF1Header header;
 
+    header.version = container[2];
+
     for (auto type_offset = header_size;
          type_offset < header_size + section_headers[TYPE_SECTION][0]; type_offset += 4)
     {
diff --git a/lib/evmone/eof.hpp b/lib/evmone/eof.hpp
index 75febe7fdc..dea647c163 100644
--- a/lib/evmone/eof.hpp
+++ b/lib/evmone/eof.hpp
@@ -28,6 +28,9 @@ struct EOFCodeType
 
 struct EOF1Header
 {
+    /// The EOF version, 0 means legacy code.
+    uint8_t version = 0;
+
     /// Size of every code section.
     std::vector<uint16_t> code_sizes;
 
diff --git a/lib/evmone/instructions.hpp b/lib/evmone/instructions.hpp
index 6a95e48662..6c764b12dc 100644
--- a/lib/evmone/instructions.hpp
+++ b/lib/evmone/instructions.hpp
@@ -933,11 +933,26 @@ evmc_status_code create_impl(StackTop stack, ExecutionState& state) noexcept;
 inline constexpr auto create = create_impl<OP_CREATE>;
 inline constexpr auto create2 = create_impl<OP_CREATE2>;
 
-inline code_iterator callf(StackTop /*stack*/, ExecutionState& state, code_iterator pos) noexcept
+inline code_iterator callf(StackTop stack, ExecutionState& state, code_iterator pos) noexcept
 {
     const auto index = read_uint16_be(&pos[1]);
+    const auto& header = state.analysis.baseline->eof_header;
+    const auto stack_size = &stack.top() - state.stack_space.bottom();
+    if (stack_size + header.types[index].max_stack_height > StackSpace::limit)
+    {
+        state.status = EVMC_STACK_OVERFLOW;
+        return nullptr;
+    }
+
+    if (state.call_stack.size() >= StackSpace::limit)
+    {
+        // TODO: Add different error code.
+        state.status = EVMC_STACK_OVERFLOW;
+        return nullptr;
+    }
     state.call_stack.push_back(pos + 3);
-    const auto offset = state.analysis.baseline->code_offsets[index];
+
+    const auto offset = header.code_offsets[index] - header.code_offsets[0];
     auto code = state.analysis.baseline->executable_code;
     return code.data() + offset;
 }
diff --git a/test/unittests/evm_eof_function_test.cpp b/test/unittests/evm_eof_function_test.cpp
index df185c6672..d02b696ee8 100644
--- a/test/unittests/evm_eof_function_test.cpp
+++ b/test/unittests/evm_eof_function_test.cpp
@@ -57,3 +57,70 @@ TEST_P(evm, eof_function_example2)
     EXPECT_GAS_USED(EVMC_SUCCESS, 44544);
     EXPECT_EQ(output, "0000000000000000000000000000000000000000000000000000000000000262"_hex);
 }
+
+TEST_P(evm, callf_stack_size_1024)
+{
+    // CALLF is not implemented in Advanced.
+    if (is_advanced())
+        return;
+
+    rev = EVMC_CANCUN;
+    const auto code = bytecode{"ef0001 010008 020002 0BFF 0004 030000 00 000003FF 00000001"_hex} +
+                      1023 * push(1) + OP_CALLF + bytecode{"0x0001"_hex} + 1021 * OP_POP +
+                      OP_RETURN + push(1) + OP_POP + OP_RETF;
+
+    ASSERT_EQ(evmone::validate_eof(rev, code), evmone::EOFValidationError::success);
+    execute(bytecode{code});
+    EXPECT_STATUS(EVMC_SUCCESS);
+}
+
+TEST_P(evm, callf_stack_overflow)
+{
+    // CALLF is not implemented in Advanced.
+    if (is_advanced())
+        return;
+
+    rev = EVMC_CANCUN;
+    const auto code =
+        bytecode{"ef0001 010008 020002 0BFF 0007 030000 00 000003FF 00000002"_hex} +  // EOF header
+        1023 * push(1) + OP_CALLF + bytecode{"0x0001"_hex} + 1021 * OP_POP + OP_RETURN +
+        2 * push(1) + 2 * OP_POP + OP_RETF;
+
+    ASSERT_EQ(evmone::validate_eof(rev, code), evmone::EOFValidationError::success);
+    execute(bytecode{code});
+    EXPECT_STATUS(EVMC_STACK_OVERFLOW);
+}
+
+TEST_P(evm, callf_call_stack_size_1024)
+{
+    // CALLF is not implemented in Advanced.
+    if (is_advanced())
+        return;
+
+    rev = EVMC_CANCUN;
+    const auto code = bytecode{"ef0001 010008 020002 0007 000e 030000 00 00000001 01000002"_hex} +
+                      push(1023) + OP_CALLF + bytecode{"0x0001"_hex} + OP_STOP + OP_DUP1 +
+                      OP_RJUMPI + bytecode{"0x0002"_hex} + OP_POP + OP_RETF + push(1) + OP_SWAP1 +
+                      OP_SUB + OP_CALLF + bytecode{"0x0001"_hex} + OP_RETF;
+
+    ASSERT_EQ(evmone::validate_eof(rev, code), evmone::EOFValidationError::success);
+    execute(bytecode{code});
+    EXPECT_STATUS(EVMC_SUCCESS);
+}
+
+TEST_P(evm, callf_call_stack_size_1025)
+{
+    // CALLF is not implemented in Advanced.
+    if (is_advanced())
+        return;
+
+    rev = EVMC_CANCUN;
+    const auto code = bytecode{"ef0001 010008 020002 0007 000e 030000 00 00000001 01000002"_hex} +
+                      push(1024) + OP_CALLF + bytecode{"0x0001"_hex} + OP_STOP + OP_DUP1 +
+                      OP_RJUMPI + bytecode{"0x0002"_hex} + OP_POP + OP_RETF + push(1) + OP_SWAP1 +
+                      OP_SUB + OP_CALLF + bytecode{"0x0001"_hex} + OP_RETF;
+
+    ASSERT_EQ(evmone::validate_eof(rev, code), evmone::EOFValidationError::success);
+    execute(bytecode{code});
+    EXPECT_STATUS(EVMC_STACK_OVERFLOW);
+}