惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

C
Cisco Blogs
罗磊的独立博客
D
Docker
Microsoft Azure Blog
Microsoft Azure Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
V
V2EX
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Cisco Talos Blog
Cisco Talos Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
I
InfoQ
S
Securelist
K
Kaspersky official blog
博客园 - 司徒正美
爱范儿
爱范儿
Scott Helme
Scott Helme
B
Blog RSS Feed
H
Help Net Security
博客园 - 聂微东
Hugging Face - Blog
Hugging Face - Blog
Stack Overflow Blog
Stack Overflow Blog
AI
AI
Blog — PlanetScale
Blog — PlanetScale
Webroot Blog
Webroot Blog
P
Proofpoint News Feed
V
Visual Studio Blog
Cyberwarzone
Cyberwarzone
P
Privacy International News Feed
M
MIT News - Artificial intelligence
Google DeepMind News
Google DeepMind News
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - Franky
IT之家
IT之家
云风的 BLOG
云风的 BLOG
MyScale Blog
MyScale Blog
L
LINUX DO - 热门话题
P
Palo Alto Networks Blog
S
Security Affairs
T
Threat Research - Cisco Blogs
S
Security @ Cisco Blogs
The Register - Security
The Register - Security
F
Full Disclosure
A
Arctic Wolf
C
Check Point Blog
Recent Announcements
Recent Announcements
P
Proofpoint News Feed
人人都是产品经理
人人都是产品经理
T
Tor Project blog
Latest news
Latest news
Schneier on Security
Schneier on Security
H
Hackread – Cybersecurity News, Data Breaches, AI and More

Arpit Bhayani

Temporal Primer - Building Long-Running Systems What Matters in Production RAG Structure of Every LLM Chat How LLMs Really Work Your Monolith Is Already A Distributed System Databases Were Not Designed For This BM25 JOIN Algorithms Venting at Work Comes at a Reputation Cost Why Half Your Skills Expire Every Few Years Multi-Paxos - Consensus in Distributed Databases MySQL Replication Internals Bloom Filters When You Increase Kafka Partitions Product Quantization The Q, K, V Matrices The Day I Accidentally Deleted Production How LLM Inference Works What are Blocking Queues and Why We Need Them Heartbeats in Distributed Systems How Writes Work in Apache Cassandra Redis Replication Internals How to Handle Arrogant Colleagues at Work How Does a CDN Handle Content Replication You Can't Fix Everything on Day One When Emotions Spill Over at Work Why gRPC Uses HTTP2 Meetings With No Agenda Are a Waste of Time Career Longevity Beats Constant Job Hopping Stay Relevant at Higher Salary Levels Why Distributed Systems Need Consensus Algorithms Like Raft Why Do Databases Deadlock and How Do They Resolve It Why and How Cache Locality Can Make Your Code Faster Why Eventual Consistency is Preferred in Distributed Systems Why does DNS use both UDP and TCP Should You Do a Master's My Honest Take Empathy Makes Great Engineers Unstoppable Good Mentors Build People, Not Just Skills Why You Should Always Have Back-Burner Projects Before You Push Back, Know What You're Standing On Be the One They Can Count On How Much Are People Willing to Bet on You How to Get Leadership to Say Yes to Your Project Don't Let Your Best Ideas Die in Silence Be the Person Everyone Wants to Work With The XY Problem and How to Avoid It The Startup Hiring Lie Nobody Talks About You Won't Be Promoted Unless You Ask It's Not Enough to be Right; Learn to be Heard No One Ships Great Software Alone You Don't Win by Proving Others Wrong Appreciate Generously; It Costs Nothing, But Builds Everything Your Soft Skills Aren't Soft at All Before you form an opinion, experience it Why You Need Both Curiosity and Action to Thrive A Daily Worklog Changed Everything How We Handle Mistakes Defines Us Own Your Mistakes Don't Wait. Step Up. Temporary Fixes Are Permanent Why Interviews Are Biased And What Sets You Apart Saying 'This isn't my problem' is actually the problem How to Write Effective OKRs Never Lose a Battle due to Miscommunication When In Doubt, Code It Out How to Follow Up Without Annoying People Lead Projects That Land, Execution Over Everything Abstract Thinking Will Define Your Next Decade We Engineers Suck at Task Estimation Shiny Obect Syndrome in Tech When to Change Jobs - The 3P Framework Comfort and Competition - Know When to Switch Gears Paper Notes - On-demand Container Loading in AWS Lambda Paper Notes - SQL Has Problems. We Can Fix Them Pipe Syntax In SQL Paper Notes - NanoLog - A Nanosecond Scale Logging System Don't Wait, Learn - The Best Resource is Mythical Paper Notes - WTF - The Who to Follow Service at Twitter The Unexpected Benefit of Reading Random Engineering Articles Roadmaps Are Limiting Your Growth Stop Leaving Money on the Table - Negotiate Your Job Offer Never Bad-Mouth Your Past Employers Show You're a Culture Fit Quantify your resume, Know Your Numbers The Importance of Being Likeable in Interviews Questions to Ask Your Interviewer How to Build Trust Through Collaboration Do This, Once You Are Out of the Interview Cycle Stop Pitching Ideas, Start Pitching Projects Read Those Design Docs, Even the Ones That Seem Irrelevant The Best Engineering Lessons Happen During Outages Great Engineers Start Broad LLM Summaries are Ruining Your Learning Turn System Design Interviews into Discussions Title Inflation At Work, Find Your Own Projects 6 Simple Strategies to Cracking Any Tech Interview How to Remain Unblocked Solving the Knapsack Problem with Evolutionary Algorithms Generating Pseudorandom Numbers with LFSR Local vs Global Indexes in Partitioned Databases
Python Internals - I Made Addition Unpredictable
Arpit Bhayani · 2020-01-03 · via Arpit Bhayani

Did you ever take a peek at Python’s source code? I didn’t and hence I decided to have some fun with it this week. After cloning the repository I realized how well written is the code that makes python what it is. In the process of exploring the codebase, I thought of making some changes, not big optimizations but some minor tweaks that will help me understand how Python is implemented in C and along the course learn some internals. To make things fun and interesting I thought of changing how addition work by making it incorrect and unpredictable which means a + b will internally do one of the following operations, at random

  • a + b
  • a - b
  • a * b
  • a / b
  • a ** b

After forking and cloning the source code of python, I broke down the task into following sub-tasks

  • find the entry point (the main function) of python
  • find where addition happens
  • find how to call other perform operations like subtraction, multiplication, etc on python objects.
  • write a function that picks one of the operators at random
  • write a function that applies an operator on the two operands

Before getting into how I did it, take a look below and see what it does

Random Math Operator in Python

You would see how performing addition on numbers 4 and 6 evaluates to 0, 10 and 24 depending on the operation it picked randomly.

Note, the change I made will only work when one of the operands is a variable. If the entire expression contains constants then it will be evaluated as regular infix expression.

Implementation

Operations in python work on opcodes very similar to the one that a microprocessor has. Depending on opcodes that the code is translated to, the operation is performed using operands (if required). The addition operation of python requires two operands and opcode is named BINARY_ADD and has value 23. When the executor encounters this opcode, it fetches the two operands from top of the stack, performs addition and then pushes back the result on the stack. The code snippet below will give you a good idea of what python does when it encounters BINARY_ADD.

case TARGET(BINARY_ADD): {
    PyObject *right = POP();
    PyObject *left = TOP();
    PyObject *sum;
    if (PyUnicode_CheckExact(left) &&
             PyUnicode_CheckExact(right)) {
        sum = unicode_concatenate(tstate, left, right, f, next_instr);
    }
    else {
        sum = PyNumber_Add(left, right);
    }
    SET_TOP(sum);
    ...
}

One thing to observe here is how it concatenates when both operands are unicode/string.

Checking if operands are numbers

For checking if both the operands for BINARY_ADD operation are numbers I used the predefined function named PyNumber_Check which checks if object referenced by PyObject is number or not.

if (PyNumber_Check(left) && PyNumber_Check(right)) {
        // Both the operands are numbers
}

Writing a random function

For generating random integer I used the current time in seconds from the system using datetime.h library and took modulus with the max value. The code snippet below picks a random number from [0, max).

int
get_random_number(int max) {
    return time(NULL) % max;
}

Functions to perform other operations

Similar to the function PyNumber_Add which adds two python objects (if possible), there are functions named PyNumber_Subtract, PyNumber_Multiply, PyNumber_FloorDivide, and PyNumber_Power which performs operations as suggested by their names. I wrote a util function that takes two operands and an operator and returns the resulting python object after performing the required operation.

PyObject *
binary_operate(PyObject * left, PyObject * right, char operator) {
    switch (operator) {
        case '+':
            return PyNumber_Add(left, right);
        case '-':
            return PyNumber_Subtract(left, right);
        case '*':
            return PyNumber_Multiply(left, right);
        case '/':
            return PyNumber_FloorDivide(left, right);
        case '^':
            return PyNumber_Power(left, right, Py_None);
        default:
            return NULL;
    }
}

The new BINARY_ADD implementation

Now as have everything required to make our BINARY_ADD unpredictable and following code snippet is very close to how it could be implemented.

case TARGET(BINARY_ADD): {
    PyObject *right = POP();
    PyObject *left = TOP();
    PyObject *result;
    if (PyUnicode_CheckExact(left) &&
             PyUnicode_CheckExact(right)) {
        result = unicode_concatenate(tstate, left, right, f, next_instr);
    }
    else {
        // Do this operation only when both the operands are numbers and
        // the evaluation was initiated from interactive interpreter (shell)
        if (PyNumber_Check(left) && PyNumber_Check(right)) {
            char operator = get_random_operator();
            result = binary_operate(left, right, operator);
            printf(
                "::::: %s + %s was evaluated as %s %c %s, hence to the value\n",
                ReprStr(left), ReprStr(right),
                ReprStr(left), operator, ReprStr(right)
            );
        } else {
            result = PyNumber_Add(left, right);
        }
        ...
    }
    ...
    SET_TOP(result);
    ...
}

Challenges

After making all the required changes I ran make to build my new python binary and to my surprise, the code wouldn’t build. The reason was that the function where I made the changes was called during build and initialization phases and due to incorrectness induced in the BINARY_ADD the process ended in Segmentation Faults as now it has a function that instead of adding two numbers was subtracting, multiplying, dividing and raising to power at random.

To fix this issue I had to ensure that this random picking of operator only happened when the operation is asked from the interactive shell and should continue its normal execution for others. The function that gets called during an interactive shell is PyRun_InteractiveLoopFlags and hence I started passing a flag named source to all the functions till my trail reaches the opcode evaluation flow. The value of this source is set to 1 when it is triggered from the interactive shell for others the default value passed is 0. Once I had this source field in place with the proper value being passed from various initiations, everything worked like a charm.

You can find the detailed diff at github.com/arpitbbhayani/cpython/pull/1/files.

Conclusion

It was fun to change the python’s source code, I would recommend you to do this as well. It is always better if you know how things work internally and more importantly understand the complexities that are abstracted to make the application developers’ experience seamless.

You can find the source code at arpitbbhayani/cpython/tree/01-randomized-math-operators. Feel free to fork it and make some changes of your own and share it with me. I will be thrilled to learn what you did with it.

If you want to dive deep into python’s source I highly recommend you to read realpython.com/cpython-source-code-guide/. It is an excellent guide to get you started and understand the language semantics and coding practices of a core python developer. Once you know the basics, navigating through the codebase is a walk in the park.