no strcpy either

Some time ago I mentioned that we went through the curl source code and eventually got rid of all strncpy() calls.

strncpy() is a weird function with a crappy API. It might not null terminate the destination and it pads the target buffer with zeroes. Quite frankly, most code bases are probably better off completely avoiding it because each use of it is a potential mistake.

In that particular rewrite when we made strncpy calls extinct, we made sure we would either copy the full string properly or return error. It is rare that copying a partial string is the right choice, and if it is, we can just as well memcpy it and handle the null terminator explicitly. This meant no case for using strlcpy or anything such either.

But strcpy?

strcpy however, has its valid uses and it has a less bad and confusing API. The main challenge with strcpy is that when using it we do not specify the length of the target buffer nor of the source string.

This is normally not a problem because in a C program strcpy should only be used when we have full control of both.

But normally and always are not necessarily the same thing. We are but all human and we all do mistakes. Using strcpy implies that there is at least one or maybe two, buffer size checks done prior to the function invocation. In a good situation.

Over time however – let’s imagine we have code that lives on for decades – when code is maintained, patched, improved and polished by many different authors with different mindsets and approaches, those size checks and the function invoke may glide apart. The further away from each other they go, the bigger is the risk that something happens in between that nullifies one of the checks or changes the conditions for the strcpy.

Enforce checks close to code

To make sure that the size checks cannot be separated from the copy itself we introduced a string copy replacement function the other day that takes the target buffer, target size, source buffer and source string length as arguments and only if the copy can be made and the null terminator also fits there, the operation is done.

This made it possible to implement the replacement using memcpy(). Now we can completely ban the use of strcpy in curl source code, like we already did strncpy.

Using this function version is a little more work and more cumbersome than strcpy since it needs more information, but we believe the upsides of this approach will help us have an oversight for the extra pain involved. I suppose we will see how that will fare down the road. Let’s come back in a decade and see how things developed!

void curlx_strcopy(char *dest,
size_t dsize,
const char *src,
size_t slen)
{
DEBUGASSERT(slen < dsize);
if(slen < dsize) {
memcpy(dest, src, slen);
dest[slen] = 0;
}
else if(dsize)
dest[0] = 0;
}

the strcopy source

AI slop

An additional minor positive side-effect of this change is of course that this should effectively prevent the AI chatbots to report strcpy uses in curl source code and insist it is insecure if anyone would ask (as people still apparently do). It has been proven numerous times already that strcpy in source code is like a honey pot for generating hallucinated vulnerability claims.

Still, this will just make them find something else to make up a report about, so there is probably no net gain. AI slop is not a game we can win.

A curl 2025 review

Let’s take a look back and remember some of what this year brought.

commits

At more than 3,400 commits we did 40% more commits in curl this year than any single previous year!

Since at some point during 2025, all the other authors in the project have now added more lines in total to the curl repository than I have. Meaning that out of all the lines ever added in the curl repository, I have now added less than half.

More than 150 individuals authored commits we merged during the year. Almost one hundred of them were first-timers. Thirteen authors wrote ten or more commits.

Viktor Szakats did the most number of commits per month for almost all months in 2025.

Stefan Eissing has now done the latest commit for 29% of the product source code lines – where my share is 36%.

About 598 authors have their added contributions still “surviving” in the product code. This is down from 635 at end of last year.

tests

We have 232 more tests at the end of this year compared to last December (now at 2179 separate test cases), and for the first time ever we have more than twelve test cases per thousand lines of product source code.

(Sure, counting test cases is rather pointless and weird since a single test can be small or big, simple or complex etc, but that’s the only count we have for this.)

releases

The eight releases we did through the year is a fairly average amount:

  • 8.12.0
  • 8.12.1
  • 8.13.0
  • 8.14.0
  • 8.14.1
  • 8.15.0
  • 8.16.0
  • 8.17.0

No major revolution happened this year in terms of big features or changes.

We reduced source code complexity a lot. We have stopped using some more functions we deem were often the reasons for errors or confusion. We have increased performance. We have reduced numbed of used allocations.

We added experimental support for HTTPS-RR, the DNS record.

The bugfix frequency rate beat new records towards the end of the year as nearly 450 bugfixes shipped in curl 8.17.0.

This year we started doing release candidates. For every release we upload a series of candidates before the actual release so that people can help us and test what is almost the finished version. This helps us detect and fix regressions before the final release rather than immediately after.

Command line options

We end the year with 6 more curl command line options than we had last new year’s eve; now at 273 in total.

8.17.0–knownhosts
8.16.0–out-null
–parallel-max-host
–follow
8.14.0–sigalgs
8.13.0–upload-flags
8.12.0–ssl-sessions

man page

The curl man page continued to grow; now more than 500 lines longer since last year (7090 lines), which means that even when counted number of man page lines per command line option it grew from 24.7 to 26.

Lines of code

libcurl grew with a mere 100 lines of code over the year while the command line tool got 1,150 new lines.

libcurl is now a little over 149,000 lines. The command line tool has 25,800 lines.

Most of the commits clearly went into improving the products rather than expanding them. See also the dropped support section below.

QUIC

This year OpenSSL finally introduced and shipped an API that allows QUIC stacks to use vanilla OpenSSL, starting with version 3.5.

As a direct result of this, the use of the OpenSSL QUIC stack has been marked as deprecated in curl and is queued for removal early next year.

As we also removed msh3 support during 2025, we are looking towards a 2026 with supporting only two QUIC and HTTP/3 backends in curl.

Security

This year the number of AI slop security reports for curl really exploded. The curl security team has gotten a lot of extra load because of this. We have been mentioned in media a lot during the year because of this.

The reports not evidently made with AI help have also gotten significantly worse quality wise while the total volume has increased – a lot. Also adding to our collective load.

We published nine curl CVEs during 2025, all at severity low or medium.

AI improvements

A new breed of AI-powered high quality code analyzers, primarily ZeroPath and Aisle Research, started pouring in bug reports to us with potential defects. We have fixed several hundred bugs as a direct result of those reports – so far.

This is in addition to the regular set of code analyzers we run against the code and for which we of course also fix the defects they report.

Web traffic

At the end of the year 2025 we see 79 TB of data getting transferred monthly from curl.se. This is up from 58 TB (+36%) for the exact same period last year.

We don’t have logs or analysis so we don’t know for sure what all this traffic is, but we know that only a tiny fraction is actual curl downloads. A huge portion of this traffic is clearly not human-driven.

GitHub activity

More than two hundred pull requests were opened each month in curl’s GitHub repository.

For a brief moment during the fall we reached zero open issues.

We have over 220 separate CI jobs that in the end of the year spend more than 25 CPU days per day verifying our ongoing changes.

Dashboard

The curl dashboard expanded a lot. I removed a few graphs that were not accurate anymore, but the net total change is still that we went up from 82 graphs in December 2024 to 92 separate illustrations in December 2025. Now with a total of 259 individual plots (+25).

Dropped support

We removed old/legacy things from the project this year, in an effort to remove laggards, to keep focus on what’s important and to make sure all of curl is secure.

  • Support for Visual Studio 2005 and older (removed in 8.13.0)
  • Secure Transport (removed in 8.15.0)
  • BearSSL (removed in 8.15.0)
  • msh3 (removed in 8.16.0)
  • winbuild build system (removed in 8.17.0)

Awards

It was a crazy year in this aspect (as well) and I was honored with:

I also dropped out of the Microsoft MVP program during the year, to which I was accepted into in October 2024.

Conferences / Talks

I attended these eight conferences and talked – in five countries. My talks are always related to curl in one way or another.

  • FOSDEM
  • foss-north
  • curl up
  • Open Infra Forum
  • Joy of Coding
  • FrOSCon
  • Open Source Summit Europe
  • EuroBSDCon

Podcasts

I participated on these podcasts during the year. Always related to curl.

  • Security Weekly
  • Open Source Security
  • Day Two DevOps
  • Netstack.FM
  • Software Engineering Radio
  • OsProgrammadores

20,000 issues on GitHub

The curl project moved over its source code hosting to GitHub in March 2010, but we kept the main bug tracker running like before – on Sourceforge.

It took us a few years, but in 2015 we finally ditched the Sourceforge version fully. We adopted and switched over to the pull request model and we labeled the GitHub issue tracker the official one to use for curl bugs. Announced on the curl website proper on March 9 2015.

GitHub holds issues and pull requests in the same number series, and since a few years back they also added discussions to the mix. This number is another pointless one, but it is large and even so let’s celebrate it!

Issue one in curl’s GitHub repository is from October 2010.

Issue 100 is from May 18, 2014.

Issue 500 is from Oct 20, 2015.

Issue 10,000 was created November 29, 2022. That meant 9,500 issues created in 2,597 days. 3.7 issues/day on average over seven years.

Issue 20,000 (a pull request really) was created today, on December 16, 2025. 10,000 more issues created in 1,113 days. 9 issues/day over the last three years.

The pace of which primarily new pull requests are submitted has certainly gone up over the recent years, as this graph clearly shows. (Since the current month is only half so far, the drop at the right end of the plot is quite expected.)

We work hard in the project to keep the number of open issues and pull requests low even when the frequency rises.

It can also be noted that issues and pull requests are typically closed fast. Out of the ones that are closed with instructions in the git commit message, the trend looks like below. Half of them are closed within 6 hours.

Of course, these graphs are updated daily and shown on the curl dashboard.

Note: we have not seen the AI slop tsunami in the issues and pull requests as we do on Hackerone. This growth is entirely human made and benign.